Allow JSONField encoder customization. (#6713)

This commit is contained in:
Raffaele Salmaso 2019-05-24 13:47:35 +02:00 committed by Tom Christie
parent afb678433b
commit 514033815d
4 changed files with 15 additions and 5 deletions

View File

@ -501,9 +501,10 @@ Note that the child field **must** be an instance of `CharField`, as the hstore
A field class that validates that the incoming data structure consists of valid JSON primitives. In its alternate binary mode, it will represent and validate JSON-encoded binary strings. A field class that validates that the incoming data structure consists of valid JSON primitives. In its alternate binary mode, it will represent and validate JSON-encoded binary strings.
**Signature**: `JSONField(binary)` **Signature**: `JSONField(binary, encoder)`
- `binary` - If set to `True` then the field will output and validate a JSON encoded string, rather than a primitive data structure. Defaults to `False`. - `binary` - If set to `True` then the field will output and validate a JSON encoded string, rather than a primitive data structure. Defaults to `False`.
- `encoder` - Use this JSON encoder to serialize input object. Defaults to `None`.
--- ---

View File

@ -1746,6 +1746,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)
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def get_value(self, dictionary): def get_value(self, dictionary):
@ -1767,14 +1768,14 @@ class JSONField(Field):
data = data.decode() data = data.decode()
return json.loads(data) return json.loads(data)
else: else:
json.dumps(data) json.dumps(data, cls=self.encoder)
except (TypeError, ValueError): except (TypeError, ValueError):
self.fail('invalid') self.fail('invalid')
return data return data
def to_representation(self, value): def to_representation(self, value):
if self.binary: if self.binary:
value = json.dumps(value) value = json.dumps(value, cls=self.encoder)
value = value.encode() value = value.encode()
return value return value

View File

@ -1232,6 +1232,11 @@ 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):
# Populate the `encoder` argument of `JSONField` instances generated
# for the PostgreSQL specific `JSONField`.
field_kwargs['encoder'] = getattr(model_field, 'encoder', 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
# for the PostgreSQL specific `ArrayField`. # for the PostgreSQL specific `ArrayField`.

View File

@ -13,6 +13,7 @@ from collections import OrderedDict
import django import django
import pytest import pytest
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.core.serializers.json import DjangoJSONEncoder
from django.core.validators import ( from django.core.validators import (
MaxValueValidator, MinLengthValidator, MinValueValidator MaxValueValidator, MinLengthValidator, MinValueValidator
) )
@ -452,15 +453,17 @@ class TestPosgresFieldsMapping(TestCase):
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()
json_field_with_encoder = postgres_fields.JSONField(encoder=DjangoJSONEncoder)
class TestSerializer(serializers.ModelSerializer): class TestSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = JSONFieldModel model = JSONFieldModel
fields = ['json_field'] fields = ['json_field', 'json_field_with_encoder']
expected = dedent(""" expected = dedent("""
TestSerializer(): TestSerializer():
json_field = JSONField(style={'base_template': 'textarea.html'}) json_field = JSONField(encoder=None, style={'base_template': 'textarea.html'})
json_field_with_encoder = JSONField(encoder=<class 'django.core.serializers.json.DjangoJSONEncoder'>, style={'base_template': 'textarea.html'})
""") """)
self.assertEqual(repr(TestSerializer()), expected) self.assertEqual(repr(TestSerializer()), expected)