From 2e337567a0bdd29fcef19d83a40d66206c4461fe Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Fri, 14 Sep 2018 15:01:11 -0400 Subject: [PATCH 1/4] adding support for manager class w/ test --- jsonate/json_encoder.py | 22 +++++++++++++++++++--- setup.py | 2 +- test_project/test_app/models.py | 6 +++++- test_project/test_app/tests.py | 7 ++++++- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/jsonate/json_encoder.py b/jsonate/json_encoder.py index df68cac..85138c0 100644 --- a/jsonate/json_encoder.py +++ b/jsonate/json_encoder.py @@ -12,7 +12,7 @@ from django.db.models.query import ValuesQuerySet from django.db.models.query import QuerySet -from django.db.models import Model +from django.db.models import Model, Manager from django.db.models.fields.related import ForeignKey from django.db.models.fields.files import FieldFile @@ -54,8 +54,9 @@ def jsonate_fields(model): fields = getattr(model._meta, 'jsonate_fields', all_fields) serialize = set(fields).difference(set(excluded)) - - return tuple(field for field in model._meta.fields + + # Getting all fields, including hidden fields as options + return tuple(field for field in model._meta.get_fields() if field.name in serialize) ######################### @@ -98,6 +99,21 @@ def map_queryset(obj): fields = jsonate_fields(obj.model) return obj.values(*[field.name for field in fields]) +# Managers are typically hidden fields, and must be specified via meta fields +@register_typemap(Manager) +def map_manager(obj): + qs = obj.get_queryset() + # otherwise using values is faster + if django_19: + if qs._iterable_class == ModelIterable: + fields = jsonate_fields(qs.model) + qs = qs.values(*[field.name for field in fields]) + + return list(qs) + + else: + fields = jsonate_fields(qs.model) + return qs.values(*[field.name for field in fields]) @register_typemap(Model) def map_model_instance(obj): diff --git a/setup.py b/setup.py index d7403c2..4fc0510 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='jsonate', - version='0.5.0', + version='0.5.1', author='James Robert', author_email='jiaaro@gmail.com', diff --git a/test_project/test_app/models.py b/test_project/test_app/models.py index d2038ba..0df22ee 100644 --- a/test_project/test_app/models.py +++ b/test_project/test_app/models.py @@ -8,7 +8,7 @@ from jsonate.fields import JsonateField class MyModel(models.Model): - foreign_key = models.ForeignKey(User) + foreign_key = models.ForeignKey(User, on_delete=models.CASCADE) normal_field1 = models.CharField(max_length=25, default="field1") normal_field2 = models.CharField(max_length=25, default='field2') @@ -29,6 +29,10 @@ class MyModel(models.Model): class Meta(object): jsonate_exclude = ('sensitive_field1',) +class MyModelWithRelation(models.Model): + name = models.CharField(max_length=100) + to_many = models.ManyToManyField(MyModel, related_name="many_to_my_model") + class MyModelWithJsonateField(models.Model): some_name = models.CharField(max_length=255) some_json_data = JsonateField(null=True, blank=True) diff --git a/test_project/test_app/tests.py b/test_project/test_app/tests.py index 94f7bb1..6b0ec03 100755 --- a/test_project/test_app/tests.py +++ b/test_project/test_app/tests.py @@ -19,7 +19,7 @@ from jsonate import jsonate from jsonate.django_ver import django_18 -from .models import MyModel, MyModelWithJsonateField, WithJsonateFieldExpectingList +from .models import MyModel, MyModelWithJsonateField, WithJsonateFieldExpectingList, MyModelWithRelation def destroy_media_folder(folder): @@ -41,6 +41,9 @@ def setUp(self): 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") @@ -140,6 +143,8 @@ 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), mymodel_data) self.assertJsonEqual(jsonate(MyModel.objects.all()), [mymodel_data]) From 1caa6318699786feeeda9b563e1b22780cc35fa5 Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Fri, 14 Sep 2018 15:07:06 -0400 Subject: [PATCH 2/4] committing migrations --- .../migrations/0004_auto_20180914_1341.py | 30 +++++++++++++++++++ .../migrations/0005_auto_20180914_1343.py | 26 ++++++++++++++++ .../migrations/0006_auto_20180914_1352.py | 20 +++++++++++++ .../migrations/0007_auto_20180914_1355.py | 24 +++++++++++++++ 4 files changed, 100 insertions(+) create mode 100644 test_project/test_app/migrations/0004_auto_20180914_1341.py create mode 100644 test_project/test_app/migrations/0005_auto_20180914_1343.py create mode 100644 test_project/test_app/migrations/0006_auto_20180914_1352.py create mode 100644 test_project/test_app/migrations/0007_auto_20180914_1355.py diff --git a/test_project/test_app/migrations/0004_auto_20180914_1341.py b/test_project/test_app/migrations/0004_auto_20180914_1341.py new file mode 100644 index 0000000..00022b4 --- /dev/null +++ b/test_project/test_app/migrations/0004_auto_20180914_1341.py @@ -0,0 +1,30 @@ +# -*- 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 + + +class Migration(migrations.Migration): + + dependencies = [ + ('test_app', '0003_withjsonatefieldexpectinglist'), + ] + + operations = [ + migrations.CreateModel( + name='MyModelWithRelation', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('many_to_many', models.ManyToManyField(related_name='many_to_my_model', to='test_app.MyModel')), + ], + ), + migrations.AlterField( + model_name='withjsonatefieldexpectinglist', + name='some_json_data', + field=jsonate.fields.JsonateField(default=[], validators=[test_app.models.validate_list]), + ), + ] diff --git a/test_project/test_app/migrations/0005_auto_20180914_1343.py b/test_project/test_app/migrations/0005_auto_20180914_1343.py new file mode 100644 index 0000000..af577f5 --- /dev/null +++ b/test_project/test_app/migrations/0005_auto_20180914_1343.py @@ -0,0 +1,26 @@ +# -*- 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 + + +class Migration(migrations.Migration): + + dependencies = [ + ('test_app', '0004_auto_20180914_1341'), + ] + + operations = [ + migrations.RemoveField( + model_name='mymodelwithrelation', + name='many_to_many', + ), + migrations.AddField( + model_name='mymodelwithrelation', + name='many_to_many', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='many_to_my_model', to='test_app.MyModel'), + preserve_default=False, + ), + ] diff --git a/test_project/test_app/migrations/0006_auto_20180914_1352.py b/test_project/test_app/migrations/0006_auto_20180914_1352.py new file mode 100644 index 0000000..b344278 --- /dev/null +++ b/test_project/test_app/migrations/0006_auto_20180914_1352.py @@ -0,0 +1,20 @@ +# -*- 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 + + +class Migration(migrations.Migration): + + dependencies = [ + ('test_app', '0005_auto_20180914_1343'), + ] + + operations = [ + migrations.RenameField( + model_name='mymodelwithrelation', + old_name='many_to_many', + new_name='to_many', + ), + ] diff --git a/test_project/test_app/migrations/0007_auto_20180914_1355.py b/test_project/test_app/migrations/0007_auto_20180914_1355.py new file mode 100644 index 0000000..ee4d8fb --- /dev/null +++ b/test_project/test_app/migrations/0007_auto_20180914_1355.py @@ -0,0 +1,24 @@ +# -*- 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 + + +class Migration(migrations.Migration): + + dependencies = [ + ('test_app', '0006_auto_20180914_1352'), + ] + + operations = [ + migrations.RemoveField( + model_name='mymodelwithrelation', + name='to_many', + ), + migrations.AddField( + model_name='mymodelwithrelation', + name='to_many', + field=models.ManyToManyField(related_name='many_to_my_model', to='test_app.MyModel'), + ), + ] From 044707e23130373d57add656340a5ac76918ff71 Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Fri, 14 Sep 2018 15:20:53 -0400 Subject: [PATCH 3/4] just returning queryset, and letting it get handled automatically --- jsonate/json_encoder.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/jsonate/json_encoder.py b/jsonate/json_encoder.py index 85138c0..b1cd4c2 100644 --- a/jsonate/json_encoder.py +++ b/jsonate/json_encoder.py @@ -103,17 +103,7 @@ def map_queryset(obj): @register_typemap(Manager) def map_manager(obj): qs = obj.get_queryset() - # otherwise using values is faster - if django_19: - if qs._iterable_class == ModelIterable: - fields = jsonate_fields(qs.model) - qs = qs.values(*[field.name for field in fields]) - - return list(qs) - - else: - fields = jsonate_fields(qs.model) - return qs.values(*[field.name for field in fields]) + return qs @register_typemap(Model) def map_model_instance(obj): From 285dc1c5366e387cb709cf720b6c51e1dd03d094 Mon Sep 17 00:00:00 2001 From: Kevin Chang Date: Fri, 14 Sep 2018 15:23:09 -0400 Subject: [PATCH 4/4] more simplification --- jsonate/json_encoder.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/jsonate/json_encoder.py b/jsonate/json_encoder.py index b1cd4c2..5b771dc 100644 --- a/jsonate/json_encoder.py +++ b/jsonate/json_encoder.py @@ -102,8 +102,7 @@ def map_queryset(obj): # Managers are typically hidden fields, and must be specified via meta fields @register_typemap(Manager) def map_manager(obj): - qs = obj.get_queryset() - return qs + return obj.get_queryset() @register_typemap(Model) def map_model_instance(obj):