From 7f02a813af1a301ede06bb09734d94de7c68e8bc Mon Sep 17 00:00:00 2001 From: Mattias Lindvall Date: Fri, 16 Oct 2020 17:34:08 +0200 Subject: [PATCH] Add support for field_classes on ModelSerializer --- docs/api-guide/serializers.md | 15 +++++++++++++++ rest_framework/serializers.py | 16 ++++++++++++++++ tests/test_model_serializer.py | 20 ++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index fd5dbb0e6..7d7a66efc 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -527,6 +527,21 @@ You can add extra fields to a `ModelSerializer` or override the default fields b Extra fields can correspond to any property or callable on the model. +## Specifying field classes + +In order to override the field class only, and still get the field kwargs set dynamically, you may use the `field_classes` +Meta option. + + class AccountSerializer(serializers.ModelSerializer): + class Meta: + model = Account + fields = ['account_name'] + field_classes = {'account_name': AccountNameField} + +Note that the field class that you use needs to support whatever kwargs the serializer determines to use for this field. +Typically, you want your custom field class to inherit from the appropriate built in field class. +For example, the custom `AccountNameField` could inherit from the built in `CharField`. + ## Specifying read only fields You may wish to specify multiple fields as read-only. Instead of adding each field explicitly with the `read_only=True` attribute, you may use the shortcut Meta option, `read_only_fields`. diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 49eec8259..913e89d75 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -1054,6 +1054,11 @@ class ModelSerializer(Serializer): source, info, model, depth ) + # Override field_class by classes defined in `Meta.field_classes`. + field_class = self.get_field_class( + source, info, model, field_class + ) + # Include any kwargs defined in `Meta.extra_kwargs` field_kwargs = self.include_extra_kwargs( field_kwargs, extra_field_kwargs @@ -1319,6 +1324,17 @@ class ModelSerializer(Serializer): (field_name, model_class.__name__) ) + def get_field_class(self, field_name, info, model_class, field_class): + """ + Get field class from 'field_classes', or use the default. + """ + field_classes = getattr(self.Meta, 'field_classes', {}) + + if field_name in field_classes: + return field_classes[field_name] + + return field_class + def include_extra_kwargs(self, kwargs, extra_kwargs): """ Include any 'extra_kwargs' that have been included for this field, diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 7da1b41ae..399811c7f 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -271,6 +271,26 @@ class TestRegularFieldMappings(TestCase): """) self.assertEqual(repr(TestSerializer()), expected) + def test_field_classes(self): + """ + Ensure `field_classes` do override the field class. + """ + class CustomCharField(serializers.CharField): + pass + + class TestSerializer(serializers.ModelSerializer): + class Meta: + model = RegularFieldsModel + fields = ('auto_field', 'char_field') + field_classes = {'char_field': CustomCharField} + + expected = dedent(""" + TestSerializer(): + auto_field = IntegerField(read_only=True) + char_field = CustomCharField(max_length=100) + """) + self.assertEqual(repr(TestSerializer()), expected) + def test_extra_field_kwargs(self): """ Ensure `extra_kwargs` are passed to generated fields.