Skip to content

Commit

Permalink
Fix read_only + default unique_together validation. (#5922)
Browse files Browse the repository at this point in the history
* Add test for read_only + default unique_together validation.
* Fix read_only + default validation
  • Loading branch information
carltongibson committed Apr 6, 2018
1 parent 32caca4 commit 42eb5a4
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 0 deletions.
30 changes: 30 additions & 0 deletions rest_framework/serializers.py
Expand Up @@ -441,6 +441,30 @@ def run_validation(self, data=empty):

return value

def _read_only_defaults(self):
fields = [
field for field in self.fields.values()
if (field.read_only) and (field.default != empty) and (field.source != '*') and ('.' not in field.source)
]

defaults = OrderedDict()
for field in fields:
try:
default = field.get_default()
except SkipField:
continue
defaults[field.field_name] = default

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 Down Expand Up @@ -1477,6 +1501,12 @@ def get_unique_together_validators(self):
if (field.source != '*') and ('.' not in field.source)
}

# Special Case: Add read_only fields with defaults.
field_names |= {
field.source for field in self.fields.values()
if (field.read_only) and (field.default != empty) and (field.source != '*') and ('.' not in field.source)
}

# Note that we make sure to check `unique_together` both on the
# base model class, but also on any parent classes.
validators = []
Expand Down
24 changes: 24 additions & 0 deletions tests/test_validators.py
Expand Up @@ -277,6 +277,30 @@ class Meta:
""")
assert repr(serializer) == expected

def test_read_only_fields_with_default(self):
"""
Special case of read_only + default DOES validate unique_together.
"""
class ReadOnlyFieldWithDefaultSerializer(serializers.ModelSerializer):
race_name = serializers.CharField(max_length=100, read_only=True, default='example')

class Meta:
model = UniquenessTogetherModel
fields = ('id', 'race_name', 'position')

data = {'position': 2}
serializer = ReadOnlyFieldWithDefaultSerializer(data=data)

assert len(serializer.validators) == 1
assert isinstance(serializer.validators[0], UniqueTogetherValidator)
assert serializer.validators[0].fields == ('race_name', 'position')
assert not serializer.is_valid()
assert serializer.errors == {
'non_field_errors': [
'The fields race_name, position must make a unique set.'
]
}

def test_allow_explict_override(self):
"""
Ensure validators can be explicitly removed..
Expand Down

0 comments on commit 42eb5a4

Please sign in to comment.