diff --git a/.travis.yml b/.travis.yml index bcc92ee..dcd7d65 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,13 @@ + sudo: false dist: xenial language: python python: - - "2.7" - "3.6" - "3.7" - "3.8" addons: - postgresql: "9.5" + postgresql: "10" env: global: - DJANGO_SETTINGS_MODULE=test_project.settings @@ -29,8 +29,20 @@ matrix: env: DJANGO=3.0 - python: "2.7" env: DJANGO=3.0.7 + - python: "3.7" + env: DJANGO=1.9 + - python: "3.7" + env: DJANGO=1.10 + - python: "3.7" + env: DJANGO=1.11 + - python: "3.8" + env: DJANGO=1.9 + - python: "3.8" + env: DJANGO=1.10 + - python: "3.8" + env: DJANGO=1.11 install: - - pip install django==${DJANGO} Pillow psycopg2 + - pip install django==${DJANGO} Pillow psycopg2-binary - pip install . before_script: - psql -c 'create database test_jsonate;' -U postgres diff --git a/jsonate/django_ver.py b/jsonate/django_ver.py deleted file mode 100644 index 17137f7..0000000 --- a/jsonate/django_ver.py +++ /dev/null @@ -1,3 +0,0 @@ -import django -django_19 = (django.VERSION >= (1,9)) -django_18 = (django.VERSION >= (1,8)) \ No newline at end of file diff --git a/jsonate/fields.py b/jsonate/fields.py index 47ae15f..75f4644 100644 --- a/jsonate/fields.py +++ b/jsonate/fields.py @@ -1,4 +1,3 @@ -from past.builtins import basestring try: import json except ImportError: @@ -7,20 +6,16 @@ from django.db import models from .utils import jsonate -from .django_ver import django_19 from .widgets import JsonateWidget from .form_fields import JsonateFormField class JsonateField(models.TextField): - if not django_19: - __metaclass__ = models.SubfieldBase - def _deserialize(self, value): if value == "": return None try: - if isinstance(value, basestring): + if isinstance(value, str): return json.loads(value) except ValueError: pass @@ -37,7 +32,7 @@ def get_db_prep_value(self, value, connection, prepared=False): if value == "": return None - if not isinstance(value, basestring): + if not isinstance(value, str): value = jsonate(value) return value diff --git a/jsonate/json_encoder.py b/jsonate/json_encoder.py index 5b771dc..4889be5 100644 --- a/jsonate/json_encoder.py +++ b/jsonate/json_encoder.py @@ -5,12 +5,7 @@ except ImportError: from django.utils import simplejson as json -from .django_ver import django_19 -if django_19: - from django.db.models.query import ModelIterable -else: - from django.db.models.query import ValuesQuerySet - +from django.db.models.query import ModelIterable from django.db.models.query import QuerySet from django.db.models import Model, Manager from django.db.models.fields.related import ForeignKey @@ -72,14 +67,6 @@ def map_object(obj): raise CouldntSerialize return to_json() - -if not django_19: - # Must come before map_queryset because ValuesQuerySet is - # a subclass of Queryset and will cause an infinite loop :( - @register_typemap(ValuesQuerySet) - def map_values_queryset(obj): - return list(obj) - @register_typemap(QuerySet) def map_queryset(obj): # if the model wants to serialize itself, go with that... @@ -87,17 +74,12 @@ def map_queryset(obj): return list(obj) # otherwise using values is faster - if django_19: - if obj._iterable_class == ModelIterable: - - fields = jsonate_fields(obj.model) - obj = obj.values(*[field.name for field in fields]) + if obj._iterable_class == ModelIterable: - return list(obj) - - else: fields = jsonate_fields(obj.model) - return obj.values(*[field.name for field in fields]) + obj = obj.values(*[field.name for field in fields]) + + return list(obj) # Managers are typically hidden fields, and must be specified via meta fields @register_typemap(Manager) diff --git a/jsonate/widgets.py b/jsonate/widgets.py index aa8d1e8..52ae7b4 100644 --- a/jsonate/widgets.py +++ b/jsonate/widgets.py @@ -1,4 +1,3 @@ -from past.builtins import basestring try: import json except ImportError: @@ -12,6 +11,6 @@ class JsonateWidget(forms.Textarea): def render(self, name, value, attrs=None, renderer=None): - if not isinstance(value, basestring): + if not isinstance(value, str): value = jsonate(value, indent=2) - return super(JsonateWidget, self).render(name, value, attrs, renderer) \ No newline at end of file + return super(JsonateWidget, self).render(name, value, attrs, renderer) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6f75dfc --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +psycopg2-binary==2.8.4 +django==3.0.4 +pillow==7.0.0 diff --git a/setup.py b/setup.py index 4fc0510..85cb151 100644 --- a/setup.py +++ b/setup.py @@ -2,13 +2,14 @@ setup( name='jsonate', - version='0.5.1', + version='0.7.0', author='James Robert', author_email='jiaaro@gmail.com', description=('Django library that can make ANYTHING into json'), long_description=open('README.markdown').read(), + long_description_content_type="text/markdown", license='MIT', keywords='django json templatetags', @@ -16,8 +17,7 @@ url='http://jsonate.com', install_requires=[ - "django>=1.7", - "future >= 0.16.0", + "django>=2.0", ], packages=[ diff --git a/test_project/manage.py b/test_project/manage.py index c4815f0..2e635c7 100755 --- a/test_project/manage.py +++ b/test_project/manage.py @@ -1,6 +1,4 @@ #!/usr/bin/env python -from __future__ import absolute_import - import os import sys diff --git a/test_project/test_app/migrations/0001_initial.py b/test_project/test_app/migrations/0001_initial.py index e5989d6..bf46577 100644 --- a/test_project/test_app/migrations/0001_initial.py +++ b/test_project/test_app/migrations/0001_initial.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.9 on 2015-12-15 14:16 -from __future__ import unicode_literals - import datetime from decimal import Decimal from django.conf import settings diff --git a/test_project/test_app/migrations/0002_mymodelwithjsonatefield.py b/test_project/test_app/migrations/0002_mymodelwithjsonatefield.py index 8b70196..2690fe9 100644 --- a/test_project/test_app/migrations/0002_mymodelwithjsonatefield.py +++ b/test_project/test_app/migrations/0002_mymodelwithjsonatefield.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - from django.db import models, migrations import jsonate.fields diff --git a/test_project/test_app/migrations/0003_withjsonatefieldexpectinglist.py b/test_project/test_app/migrations/0003_withjsonatefieldexpectinglist.py index 04e44ed..6aa3b48 100644 --- a/test_project/test_app/migrations/0003_withjsonatefieldexpectinglist.py +++ b/test_project/test_app/migrations/0003_withjsonatefieldexpectinglist.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - from django.db import models, migrations import jsonate.fields import test_app.models diff --git a/test_project/test_app/migrations/0004_auto_20180914_1341.py b/test_project/test_app/migrations/0004_auto_20180914_1341.py index 00022b4..d0084d4 100644 --- a/test_project/test_app/migrations/0004_auto_20180914_1341.py +++ b/test_project/test_app/migrations/0004_auto_20180914_1341.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.15 on 2018-09-14 13:41 -from __future__ import unicode_literals - from django.db import migrations, models import jsonate.fields import test_app.models diff --git a/test_project/test_app/migrations/0005_auto_20180914_1343.py b/test_project/test_app/migrations/0005_auto_20180914_1343.py index af577f5..2285646 100644 --- a/test_project/test_app/migrations/0005_auto_20180914_1343.py +++ b/test_project/test_app/migrations/0005_auto_20180914_1343.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.15 on 2018-09-14 13:43 -from __future__ import unicode_literals - from django.db import migrations, models import django.db.models.deletion diff --git a/test_project/test_app/migrations/0006_auto_20180914_1352.py b/test_project/test_app/migrations/0006_auto_20180914_1352.py index b344278..108a75f 100644 --- a/test_project/test_app/migrations/0006_auto_20180914_1352.py +++ b/test_project/test_app/migrations/0006_auto_20180914_1352.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.15 on 2018-09-14 13:52 -from __future__ import unicode_literals - from django.db import migrations diff --git a/test_project/test_app/migrations/0007_auto_20180914_1355.py b/test_project/test_app/migrations/0007_auto_20180914_1355.py index ee4d8fb..36cdaf8 100644 --- a/test_project/test_app/migrations/0007_auto_20180914_1355.py +++ b/test_project/test_app/migrations/0007_auto_20180914_1355.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.11.15 on 2018-09-14 13:55 -from __future__ import unicode_literals - from django.db import migrations, models diff --git a/test_project/test_app/models.py b/test_project/test_app/models.py index 0df22ee..0cb6df3 100644 --- a/test_project/test_app/models.py +++ b/test_project/test_app/models.py @@ -1,4 +1,3 @@ -from builtins import object from datetime import datetime, date from decimal import Decimal @@ -26,7 +25,7 @@ class MyModel(models.Model): boolean_field = models.BooleanField(default=True) null_field = models.NullBooleanField(default=None) - class Meta(object): + class Meta: jsonate_exclude = ('sensitive_field1',) class MyModelWithRelation(models.Model): diff --git a/test_project/test_app/tests.py b/test_project/test_app/tests.py index f9d94c3..6cfe1f8 100755 --- a/test_project/test_app/tests.py +++ b/test_project/test_app/tests.py @@ -1,9 +1,3 @@ -from __future__ import unicode_literals - -from builtins import range -from past.builtins import basestring -from builtins import object - from os import unlink, rmdir from os.path import join from glob import glob @@ -17,45 +11,56 @@ from django.forms import ModelForm from jsonate import jsonate -from jsonate.django_ver import django_18 -from .models import MyModel, MyModelWithJsonateField, WithJsonateFieldExpectingList, MyModelWithRelation +from .models import ( + MyModel, MyModelWithJsonateField, WithJsonateFieldExpectingList, + MyModelWithRelation +) def destroy_media_folder(folder): path = join(settings.MEDIA_ROOT, folder) [unlink(f) for f in glob(join(path, "*"))] - try: rmdir(path) - except: pass + try: + rmdir(path) + except: + pass + +class JsonateTests(TestCase): + maxDiff = 10 ** 4 -class JsonateTests(TestCase): - maxDiff = 10**4 def setUp(self): destroy_media_folder("files") destroy_media_folder("images") - - self.user = User.objects.create_user("asdf", 'asdf@example.com', "password") + + self.user = User.objects.create_user( + "asdf", 'asdf@example.com', "password" + ) self.model = MyModel( - foreign_key=self.user - ) - self.model.file_field.save("text_file.txt", ContentFile("Any Old Content")) - self.model.image_field.save("image_file.wbm", ContentFile('\x00\x00\x01\x01\x80')) + foreign_key=self.user + ) + self.model.file_field.save( + "text_file.txt", ContentFile("Any Old Content") + ) + self.model.image_field.save( + "image_file.wbm", ContentFile('\x00\x00\x01\x01\x80') + ) self.model.save() self.related_model = MyModelWithRelation(name="related_model") self.related_model.save() self.related_model.to_many.add(self.model) - + def tearDown(self): destroy_media_folder("files") destroy_media_folder("images") - + def assertJsonEqual(self, obj1, obj2, *args, **kwargs): obj1 = json.loads(obj1) - - if isinstance(obj2, basestring): + + if isinstance(obj2, str): obj2 = json.loads(obj2) - + self.assertEqual(obj1, obj2, *args, **kwargs) def test_jsonate_field(self): @@ -63,7 +68,7 @@ def assertJSONField(to_write): obj.some_json_data = to_write obj.save() - expected = json.loads(to_write) if isinstance(to_write, basestring) else to_write + expected = json.loads(to_write) if isinstance(to_write, str) else to_write self.assertEqual( MyModelWithJsonateField.objects.first().some_json_data, @@ -74,8 +79,9 @@ def assertJSONField(to_write): test_data = [ None, - {"red":3, "orange":451}, - [{"red":3, "orange":451}, {"green":"dark", "white":"bright"}, None, ["A", "B"]], + {"red": 3, "orange": 451}, + [{"red": 3, "orange": 451}, {"green": "dark", "white": "bright"}, + None, ["A", "B"]], '["house", "mouse", "strauss"]' ] @@ -87,7 +93,7 @@ def assertJSONField(to_write): def assertJsonateFieldForm(self, model_class, data_to_store): class JsonateFieldForm(ModelForm): - class Meta(object): + class Meta: model = model_class fields = '__all__' @@ -105,26 +111,29 @@ class Meta(object): ) def test_jsonate_field_clean_form(self): - dict_to_store = {"red":3, "orange":451} + dict_to_store = {"red": 3, "orange": 451} self.assertJsonateFieldForm(MyModelWithJsonateField, dict_to_store) def test_jsonate_field_clean_form_with_validation(self): list_to_store = ["house", "mouse", "strauss"] - self.assertJsonateFieldForm(WithJsonateFieldExpectingList, list_to_store) + self.assertJsonateFieldForm(WithJsonateFieldExpectingList, + list_to_store) def test_jsonate_field_in_values_list_gets_deserialized(self): expected = [] for i in range(0, 5): - - to_create = {"some_name": "name{}".format(i), "some_json_data": {"item_{}".format(i): i}} + to_create = { + "some_name": f"name{i}", "some_json_data": {f"item_{i}": i} + } MyModelWithJsonateField.objects.create(**to_create) - if django_18: - expected.append((to_create["some_name"], to_create["some_json_data"])) - else: - expected.append((to_create["some_name"], json.dumps(to_create["some_json_data"]))) + expected.append( + (to_create["some_name"], to_create["some_json_data"]) + ) - vl = MyModelWithJsonateField.objects.order_by("id").values_list("some_name", "some_json_data") + vl = MyModelWithJsonateField.objects.order_by("id").values_list( + "some_name", "some_json_data" + ) for (index, elem) in enumerate(vl): self.assertEqual(elem, expected[index]) @@ -144,11 +153,14 @@ def test_basic_serialization(self): "file_field": "files/text_file.txt" } - self.assertJsonEqual(jsonate(self.model.many_to_my_model), [{"id": 1, "name": "related_model"}]) + self.assertJsonEqual( + jsonate(self.model.many_to_my_model), + [{"id": 1, "name": "related_model"}] + ) self.assertJsonEqual(jsonate(self.model), mymodel_data) self.assertJsonEqual(jsonate(MyModel.objects.all()), [mymodel_data]) - + user_data_values = [{ "username": 'asdf', "password": self.user.password @@ -170,6 +182,7 @@ def test_basic_serialization(self): user_data_values_list_flat ) + from django.core import management if __name__ == "__main__":