Repository: encode/django-rest-framework

Issue:
ModelSerializer Missing support for Django 3.1 JSONField
## Checklist

- [x] I have verified that that issue exists against the `master` branch of Django REST framework.
- [x] I have searched for similar issues in both open and closed tickets and cannot find a duplicate.
- [x] This is not a usage question. (Those should be directed to the [discussion group](https://groups.google.com/forum/#!forum/django-rest-framework) instead.)
- [x] This cannot be dealt with as a third party library. (We prefer new functionality to be [in the form of third party libraries](https://www.django-rest-framework.org/community/third-party-packages/#about-third-party-packages) where possible.)
- [x] I have reduced the issue to the simplest possible case.
- [ ] I have included a failing test as a pull request. (If you are unable to do so we can still accept the issue.)

## Steps to reproduce

Django 3.1 includes a new generic JSONField to replace the PostgreSQL-specific one:
https://docs.djangoproject.com/en/3.1/releases/3.1/#jsonfield-for-all-supported-database-backends

This is missing in ModelSerializer's field mapping, so a `django.db.models.JSONField` does not get mapped to a JSONField in the serializer.

## Expected behavior

The serializer behaves the same as when using the postgresql version of the field.

## Actual behavior

The new field type is treated as a text field (including schema generation, etc)


Discussion:
Yes, this will need updating (but it should be straightforward I'm thinking 🤔) 

Would be a good PR.

Resolving patch:
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -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 @@ def to_internal_value(self, data):
             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):
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -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 @@ def build_standard_field(self, field_name, model_field):
             # `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
diff --git a/rest_framework/utils/field_mapping.py b/rest_framework/utils/field_mapping.py
--- a/rest_framework/utils/field_mapping.py
+++ b/rest_framework/utils/field_mapping.py
@@ -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:


Test patch:
diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py
--- a/tests/test_model_serializer.py
+++ b/tests/test_model_serializer.py
@@ -7,6 +7,7 @@
 """
 import datetime
 import decimal
+import json  # noqa
 import sys
 import tempfile
 from collections import OrderedDict
@@ -478,6 +479,7 @@ class Meta:
         """)
         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 Meta:
         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.
 # ------------------------------------