Skip to content

Commit

Permalink
Fixing #6053: injection of read-only default values is done too late
Browse files Browse the repository at this point in the history
  • Loading branch information
vdel committed Oct 15, 2018
1 parent 1c3f796 commit 987a7c1
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 10 deletions.
25 changes: 15 additions & 10 deletions rest_framework/serializers.py
Expand Up @@ -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 [
Expand Down Expand Up @@ -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.
Expand All @@ -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:
Expand All @@ -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)

Expand Down
29 changes: 29 additions & 0 deletions tests/test_serializer.py
Expand Up @@ -613,3 +613,32 @@ class Grandchild(Child):
assert len(Parent().get_fields()) == 2
assert len(Child().get_fields()) == 2
assert len(Grandchild().get_fields()) == 2


class Test5922Regression:
class Point(object):
def __init__(self, srid, x, y):
self.srid = srid
self.coords = (x, y)

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 == {}

0 comments on commit 987a7c1

Please sign in to comment.