Skip to content

Commit 42eb5a4

Browse files
Fix read_only + default unique_together validation. (#5922)
* Add test for read_only + default unique_together validation. * Fix read_only + default validation
1 parent 32caca4 commit 42eb5a4

File tree

2 files changed

+54
-0
lines changed

2 files changed

+54
-0
lines changed

Diff for: rest_framework/serializers.py

+30
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,30 @@ def run_validation(self, data=empty):
441441

442442
return value
443443

444+
def _read_only_defaults(self):
445+
fields = [
446+
field for field in self.fields.values()
447+
if (field.read_only) and (field.default != empty) and (field.source != '*') and ('.' not in field.source)
448+
]
449+
450+
defaults = OrderedDict()
451+
for field in fields:
452+
try:
453+
default = field.get_default()
454+
except SkipField:
455+
continue
456+
defaults[field.field_name] = default
457+
458+
return defaults
459+
460+
def run_validators(self, value):
461+
"""
462+
Add read_only fields with defaults to value before running validators.
463+
"""
464+
to_validate = self._read_only_defaults()
465+
to_validate.update(value)
466+
super(Serializer, self).run_validators(to_validate)
467+
444468
def to_internal_value(self, data):
445469
"""
446470
Dict of native values <- Dict of primitive datatypes.
@@ -1477,6 +1501,12 @@ def get_unique_together_validators(self):
14771501
if (field.source != '*') and ('.' not in field.source)
14781502
}
14791503

1504+
# Special Case: Add read_only fields with defaults.
1505+
field_names |= {
1506+
field.source for field in self.fields.values()
1507+
if (field.read_only) and (field.default != empty) and (field.source != '*') and ('.' not in field.source)
1508+
}
1509+
14801510
# Note that we make sure to check `unique_together` both on the
14811511
# base model class, but also on any parent classes.
14821512
validators = []

Diff for: tests/test_validators.py

+24
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,30 @@ class Meta:
277277
""")
278278
assert repr(serializer) == expected
279279

280+
def test_read_only_fields_with_default(self):
281+
"""
282+
Special case of read_only + default DOES validate unique_together.
283+
"""
284+
class ReadOnlyFieldWithDefaultSerializer(serializers.ModelSerializer):
285+
race_name = serializers.CharField(max_length=100, read_only=True, default='example')
286+
287+
class Meta:
288+
model = UniquenessTogetherModel
289+
fields = ('id', 'race_name', 'position')
290+
291+
data = {'position': 2}
292+
serializer = ReadOnlyFieldWithDefaultSerializer(data=data)
293+
294+
assert len(serializer.validators) == 1
295+
assert isinstance(serializer.validators[0], UniqueTogetherValidator)
296+
assert serializer.validators[0].fields == ('race_name', 'position')
297+
assert not serializer.is_valid()
298+
assert serializer.errors == {
299+
'non_field_errors': [
300+
'The fields race_name, position must make a unique set.'
301+
]
302+
}
303+
280304
def test_allow_explict_override(self):
281305
"""
282306
Ensure validators can be explicitly removed..

0 commit comments

Comments
 (0)