mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-11-04 09:57:55 +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):
 | 
			
		||||
        self.binary = kwargs.pop('binary', False)
 | 
			
		||||
        self.encoder = kwargs.pop('encoder', None)
 | 
			
		||||
        self.decoder = kwargs.pop('decoder', None)
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def get_value(self, dictionary):
 | 
			
		||||
| 
						 | 
				
			
			@ -1777,7 +1778,7 @@ class JSONField(Field):
 | 
			
		|||
            if self.binary or getattr(data, 'is_json_string', False):
 | 
			
		||||
                if isinstance(data, bytes):
 | 
			
		||||
                    data = data.decode()
 | 
			
		||||
                return json.loads(data)
 | 
			
		||||
                return json.loads(data, cls=self.decoder)
 | 
			
		||||
            else:
 | 
			
		||||
                json.dumps(data, cls=self.encoder)
 | 
			
		||||
        except (TypeError, ValueError):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -884,6 +884,8 @@ class ModelSerializer(Serializer):
 | 
			
		|||
        models.GenericIPAddressField: IPAddressField,
 | 
			
		||||
        models.FilePathField: FilePathField,
 | 
			
		||||
    }
 | 
			
		||||
    if hasattr(models, 'JSONField'):
 | 
			
		||||
        serializer_field_mapping[models.JSONField] = JSONField
 | 
			
		||||
    if postgres_fields:
 | 
			
		||||
        serializer_field_mapping[postgres_fields.HStoreField] = HStoreField
 | 
			
		||||
        serializer_field_mapping[postgres_fields.ArrayField] = ListField
 | 
			
		||||
| 
						 | 
				
			
			@ -1242,10 +1244,13 @@ class ModelSerializer(Serializer):
 | 
			
		|||
            # `allow_blank` is only valid for textual fields.
 | 
			
		||||
            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
 | 
			
		||||
            # for the PostgreSQL specific `JSONField`.
 | 
			
		||||
            # for the model `JSONField`.
 | 
			
		||||
            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):
 | 
			
		||||
            # 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
 | 
			
		||||
 | 
			
		||||
    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'}
 | 
			
		||||
 | 
			
		||||
    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 decimal
 | 
			
		||||
import json  # noqa
 | 
			
		||||
import sys
 | 
			
		||||
import tempfile
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
| 
						 | 
				
			
			@ -478,6 +479,7 @@ class TestPosgresFieldsMapping(TestCase):
 | 
			
		|||
        """)
 | 
			
		||||
        self.assertEqual(repr(TestSerializer()), expected)
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.skipif(hasattr(models, 'JSONField'), reason='has models.JSONField')
 | 
			
		||||
    def test_json_field(self):
 | 
			
		||||
        class JSONFieldModel(models.Model):
 | 
			
		||||
            json_field = postgres_fields.JSONField()
 | 
			
		||||
| 
						 | 
				
			
			@ -496,6 +498,30 @@ class TestPosgresFieldsMapping(TestCase):
 | 
			
		|||
        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.
 | 
			
		||||
# ------------------------------------
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user