mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-02-09 08:00:52 +03:00
Add support for Django 3.1 JSONField (#7467)
Django 3.1 adds a new generic JSONField to replace the PostgreSQL-specific one. This adds support for the new field type, which should behave the same as the existing PostgreSQL field. Django's new JSONField also includes support for a custom "decoder", so add support for that in the serializer field.
This commit is contained in:
parent
7f3a3557a0
commit
b3e02592d0
|
@ -1758,6 +1758,7 @@ class JSONField(Field):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.binary = kwargs.pop('binary', False)
|
self.binary = kwargs.pop('binary', False)
|
||||||
self.encoder = kwargs.pop('encoder', None)
|
self.encoder = kwargs.pop('encoder', None)
|
||||||
|
self.decoder = kwargs.pop('decoder', None)
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def get_value(self, dictionary):
|
def get_value(self, dictionary):
|
||||||
|
@ -1777,7 +1778,7 @@ class JSONField(Field):
|
||||||
if self.binary or getattr(data, 'is_json_string', False):
|
if self.binary or getattr(data, 'is_json_string', False):
|
||||||
if isinstance(data, bytes):
|
if isinstance(data, bytes):
|
||||||
data = data.decode()
|
data = data.decode()
|
||||||
return json.loads(data)
|
return json.loads(data, cls=self.decoder)
|
||||||
else:
|
else:
|
||||||
json.dumps(data, cls=self.encoder)
|
json.dumps(data, cls=self.encoder)
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
|
|
|
@ -884,6 +884,8 @@ class ModelSerializer(Serializer):
|
||||||
models.GenericIPAddressField: IPAddressField,
|
models.GenericIPAddressField: IPAddressField,
|
||||||
models.FilePathField: FilePathField,
|
models.FilePathField: FilePathField,
|
||||||
}
|
}
|
||||||
|
if hasattr(models, 'JSONField'):
|
||||||
|
serializer_field_mapping[models.JSONField] = JSONField
|
||||||
if postgres_fields:
|
if postgres_fields:
|
||||||
serializer_field_mapping[postgres_fields.HStoreField] = HStoreField
|
serializer_field_mapping[postgres_fields.HStoreField] = HStoreField
|
||||||
serializer_field_mapping[postgres_fields.ArrayField] = ListField
|
serializer_field_mapping[postgres_fields.ArrayField] = ListField
|
||||||
|
@ -1242,10 +1244,13 @@ class ModelSerializer(Serializer):
|
||||||
# `allow_blank` is only valid for textual fields.
|
# `allow_blank` is only valid for textual fields.
|
||||||
field_kwargs.pop('allow_blank', None)
|
field_kwargs.pop('allow_blank', None)
|
||||||
|
|
||||||
if postgres_fields and isinstance(model_field, postgres_fields.JSONField):
|
is_django_jsonfield = hasattr(models, 'JSONField') and isinstance(model_field, models.JSONField)
|
||||||
|
if (postgres_fields and isinstance(model_field, postgres_fields.JSONField)) or is_django_jsonfield:
|
||||||
# Populate the `encoder` argument of `JSONField` instances generated
|
# Populate the `encoder` argument of `JSONField` instances generated
|
||||||
# for the PostgreSQL specific `JSONField`.
|
# for the model `JSONField`.
|
||||||
field_kwargs['encoder'] = getattr(model_field, 'encoder', None)
|
field_kwargs['encoder'] = getattr(model_field, 'encoder', None)
|
||||||
|
if is_django_jsonfield:
|
||||||
|
field_kwargs['decoder'] = getattr(model_field, 'decoder', None)
|
||||||
|
|
||||||
if postgres_fields and isinstance(model_field, postgres_fields.ArrayField):
|
if postgres_fields and isinstance(model_field, postgres_fields.ArrayField):
|
||||||
# Populate the `child` argument on `ListField` instances generated
|
# Populate the `child` argument on `ListField` instances generated
|
||||||
|
|
|
@ -92,7 +92,8 @@ def get_field_kwargs(field_name, model_field):
|
||||||
kwargs['allow_unicode'] = model_field.allow_unicode
|
kwargs['allow_unicode'] = model_field.allow_unicode
|
||||||
|
|
||||||
if isinstance(model_field, models.TextField) and not model_field.choices or \
|
if isinstance(model_field, models.TextField) and not model_field.choices or \
|
||||||
(postgres_fields and isinstance(model_field, postgres_fields.JSONField)):
|
(postgres_fields and isinstance(model_field, postgres_fields.JSONField)) or \
|
||||||
|
(hasattr(models, 'JSONField') and isinstance(model_field, models.JSONField)):
|
||||||
kwargs['style'] = {'base_template': 'textarea.html'}
|
kwargs['style'] = {'base_template': 'textarea.html'}
|
||||||
|
|
||||||
if isinstance(model_field, models.AutoField) or not model_field.editable:
|
if isinstance(model_field, models.AutoField) or not model_field.editable:
|
||||||
|
|
|
@ -7,6 +7,7 @@ an appropriate set of serializer fields for each case.
|
||||||
"""
|
"""
|
||||||
import datetime
|
import datetime
|
||||||
import decimal
|
import decimal
|
||||||
|
import json # noqa
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
@ -478,6 +479,7 @@ class TestPosgresFieldsMapping(TestCase):
|
||||||
""")
|
""")
|
||||||
self.assertEqual(repr(TestSerializer()), expected)
|
self.assertEqual(repr(TestSerializer()), expected)
|
||||||
|
|
||||||
|
@pytest.mark.skipif(hasattr(models, 'JSONField'), reason='has models.JSONField')
|
||||||
def test_json_field(self):
|
def test_json_field(self):
|
||||||
class JSONFieldModel(models.Model):
|
class JSONFieldModel(models.Model):
|
||||||
json_field = postgres_fields.JSONField()
|
json_field = postgres_fields.JSONField()
|
||||||
|
@ -496,6 +498,30 @@ class TestPosgresFieldsMapping(TestCase):
|
||||||
self.assertEqual(repr(TestSerializer()), expected)
|
self.assertEqual(repr(TestSerializer()), expected)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomJSONDecoder(json.JSONDecoder):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not hasattr(models, 'JSONField'), reason='no models.JSONField')
|
||||||
|
class TestDjangoJSONFieldMapping(TestCase):
|
||||||
|
def test_json_field(self):
|
||||||
|
class JSONFieldModel(models.Model):
|
||||||
|
json_field = models.JSONField()
|
||||||
|
json_field_with_encoder = models.JSONField(encoder=DjangoJSONEncoder, decoder=CustomJSONDecoder)
|
||||||
|
|
||||||
|
class TestSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = JSONFieldModel
|
||||||
|
fields = ['json_field', 'json_field_with_encoder']
|
||||||
|
|
||||||
|
expected = dedent("""
|
||||||
|
TestSerializer():
|
||||||
|
json_field = JSONField(decoder=None, encoder=None, style={'base_template': 'textarea.html'})
|
||||||
|
json_field_with_encoder = JSONField(decoder=<class 'tests.test_model_serializer.CustomJSONDecoder'>, encoder=<class 'django.core.serializers.json.DjangoJSONEncoder'>, style={'base_template': 'textarea.html'})
|
||||||
|
""")
|
||||||
|
self.assertEqual(repr(TestSerializer()), expected)
|
||||||
|
|
||||||
|
|
||||||
# Tests for relational field mappings.
|
# Tests for relational field mappings.
|
||||||
# ------------------------------------
|
# ------------------------------------
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user