Add MODEL_SERIALIZER_FIELD_MAPPING settings

This commit is contained in:
Stanislav Khlud 2024-08-21 09:55:29 +07:00
parent f113ab6b68
commit 760921a9bb
No known key found for this signature in database
GPG Key ID: 4A9896D700E79EB7
4 changed files with 115 additions and 43 deletions

View File

@ -143,6 +143,17 @@ Default: `ordering`
---
## Serializer settings
#### MODEL_SERIALIZER_FIELD_MAPPING
Extra field mapping used to extend or override mapping of django db fields to serializer fields which is used by
ModelSerializer to set up fields for serializer.
Default: `{}`
---
## Versioning settings
#### DEFAULT_VERSION

View File

@ -894,13 +894,35 @@ class ModelSerializer(Serializer):
* A set of default validators are automatically populated.
* Default `.create()` and `.update()` implementations are provided.
"""
serializer_related_field = PrimaryKeyRelatedField
serializer_related_to_field = SlugRelatedField
serializer_url_field = HyperlinkedIdentityField
serializer_choice_field = ChoiceField
# The field name for hyperlinked identity fields. Defaults to 'url'.
# You can modify this using the API setting.
#
# Note that if you instead need modify this on a per-serializer basis,
# you'll also need to ensure you update the `create` method on any generic
# views, to correctly handle the 'Location' response header for
# "HTTP 201 Created" responses.
url_field_name = None
@property
def serializer_field_mapping(self):
"""Get mapping of django model field to serializer field.
The process of automatically determining a set of serializer fields
based on the model fields is reasonably complex, but you almost certainly
don't need to dig into the implementation.
If the `ModelSerializer` class *doesn't* generate the set of fields that
you need you should either declare the extra/differing fields explicitly on
the serializer class, or simply use a `Serializer` class.
you need you should either extend serializer_field_mapping with
the extra/differing fields explicitly, or simply use a `Serializer`
class.
"""
serializer_field_mapping = {
models.AutoField: IntegerField,
@ -936,19 +958,12 @@ class ModelSerializer(Serializer):
serializer_field_mapping[postgres_fields.HStoreField] = HStoreField
serializer_field_mapping[postgres_fields.ArrayField] = ListField
serializer_field_mapping[postgres_fields.JSONField] = JSONField
serializer_related_field = PrimaryKeyRelatedField
serializer_related_to_field = SlugRelatedField
serializer_url_field = HyperlinkedIdentityField
serializer_choice_field = ChoiceField
# The field name for hyperlinked identity fields. Defaults to 'url'.
# You can modify this using the API setting.
#
# Note that if you instead need modify this on a per-serializer basis,
# you'll also need to ensure you update the `create` method on any generic
# views, to correctly handle the 'Location' response header for
# "HTTP 201 Created" responses.
url_field_name = None
for (
model_field,
serializer_field,
) in api_settings.MODEL_SERIALIZER_FIELD_MAPPING.items():
serializer_field_mapping[model_field] = serializer_field
return serializer_field_mapping
# Default `create` and `update` behavior...
def create(self, validated_data):

View File

@ -126,6 +126,9 @@ DEFAULTS = {
'retrieve': 'read',
'destroy': 'delete'
},
# Serializers
'MODEL_SERIALIZER_FIELD_MAPPING': {}
}
@ -147,7 +150,8 @@ IMPORT_STRINGS = [
'UNAUTHENTICATED_USER',
'UNAUTHENTICATED_TOKEN',
'VIEW_NAME_FUNCTION',
'VIEW_DESCRIPTION_FUNCTION'
'VIEW_DESCRIPTION_FUNCTION',
'MODEL_SERIALIZER_FIELD_MAPPING',
]
@ -168,6 +172,16 @@ def perform_import(val, setting_name):
return import_from_string(val, setting_name)
elif isinstance(val, (list, tuple)):
return [import_from_string(item, setting_name) for item in val]
elif isinstance(val, (dict)):
return {
import_from_string(
key,
setting_name,
): import_from_string(
value,
setting_name,
) for key, value in val.items()
}
return val

View File

@ -21,7 +21,7 @@ from django.core.validators import (
from django.db import models
from django.db.models.signals import m2m_changed
from django.dispatch import receiver
from django.test import TestCase
from django.test import TestCase, override_settings
from rest_framework import serializers
from rest_framework.compat import postgres_fields
@ -43,6 +43,12 @@ class CustomField(models.Field):
pass
class CustomCharFieldField(serializers.CharField):
"""
A custom serializer field simply for testing purposes.
"""
class OneFieldModel(models.Model):
char_field = models.CharField(max_length=100)
@ -194,6 +200,32 @@ class TestRegularFieldMappings(TestCase):
custom_field = ModelField\(model_field=<tests.test_model_serializer.CustomField: custom_field>\)
file_path_field = FilePathField\(path=%r\)
""" % tempfile.gettempdir())
print(expected)
assert re.search(expected, repr(TestSerializer())) is not None
@override_settings(
REST_FRAMEWORK={
'MODEL_SERIALIZER_FIELD_MAPPING': {
'django.db.models.CharField': 'tests.test_model_serializer.CustomCharFieldField',
}
},
)
def test_custom_fields(self):
"""
If MODEL_SERIALIZER_FIELD_MAPPING is set than model fields should map
to their equivalent serializer fields.
"""
class TestSerializer(serializers.ModelSerializer):
class Meta:
model = RegularFieldsModel
fields = (
"char_field",
)
expected = dedent(r"""
TestSerializer\(\):
char_field = CustomCharFieldField\(max_length=100\)
""")
assert re.search(expected, repr(TestSerializer())) is not None
def test_field_options(self):