From d15a40916b78b427e6b99937555f42ae15f2bea8 Mon Sep 17 00:00:00 2001 From: Vincent Delaitre Date: Thu, 11 Oct 2018 19:31:52 +0200 Subject: [PATCH] Fixing #6053: injection of read-only default values is done too late --- rest_framework/serializers.py | 25 +++++++++++++++---------- tests/test_serializer.py | 26 ++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 110ffbfa98d..368450f51ef 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -370,6 +370,12 @@ def _writable_fields(self): field for field in self.fields.values() if not field.read_only ] + @cached_property + def _read_only_fields(self): + return [ + field for field in self.fields.values() if field.read_only + ] + @cached_property def _readable_fields(self): return [ @@ -457,14 +463,6 @@ def _read_only_defaults(self): return defaults - def run_validators(self, value): - """ - Add read_only fields with defaults to value before running validators. - """ - to_validate = self._read_only_defaults() - to_validate.update(value) - super(Serializer, self).run_validators(to_validate) - def to_internal_value(self, data): """ Dict of native values <- Dict of primitive datatypes. @@ -479,9 +477,11 @@ def to_internal_value(self, data): ret = OrderedDict() errors = OrderedDict() - fields = self._writable_fields + writable_fields = self._writable_fields + read_only_fields = self._read_only_fields + read_only_defaults = self._read_only_defaults() - for field in fields: + for field in writable_fields: validate_method = getattr(self, 'validate_' + field.field_name, None) primitive_value = field.get_value(data) try: @@ -497,6 +497,11 @@ def to_internal_value(self, data): else: set_value(ret, field.source_attrs, validated_value) + for field in read_only_fields: + primitive_value = field.get_value(read_only_defaults) + if primitive_value is not empty: + set_value(ret, field.source_attrs, primitive_value) + if errors: raise ValidationError(errors) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 0650b7c5701..86004752e30 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -613,3 +613,29 @@ class Grandchild(Child): assert len(Parent().get_fields()) == 2 assert len(Child().get_fields()) == 2 assert len(Grandchild().get_fields()) == 2 + + +class Test5922Regression: + from django.contrib.gis.geos.point import Point + + def setup(self): + + # Declares a serializer that converts data into an object + class NestedPointSerializer(serializers.Serializer): + longitude = serializers.FloatField(source='x') + latitude = serializers.FloatField(source='y') + + def to_internal_value(self, data): + kwargs = super(NestedPointSerializer, self).to_internal_value(data) + return Test5922Regression.Point(srid=4326, **kwargs) + + self.Serializer = NestedPointSerializer + + def test_5922_regression(self): + serializer = self.Serializer(data={'longitude': 6.958307, 'latitude': 50.941357}) + assert serializer.is_valid() + assert isinstance(serializer.validated_data, Test5922Regression.Point) + assert serializer.validated_data.srid == 4326 + assert serializer.validated_data.coords[0] == 6.958307 + assert serializer.validated_data.coords[1] == 50.941357 + assert serializer.errors == {}