From 0327be56c27fb51eac9265840d04edc0f0f1e294 Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Fri, 6 Apr 2018 09:15:48 +0200 Subject: [PATCH 1/2] Add test for read_only + default unique_together validation. --- tests/test_validators.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/test_validators.py b/tests/test_validators.py index 62126ddb33..4bbddb64ba 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -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.. From 2227bc47f8b287b66775948ffb60b2d9378ac84f Mon Sep 17 00:00:00 2001 From: Carlton Gibson Date: Fri, 6 Apr 2018 12:15:30 +0200 Subject: [PATCH 2/2] Fix read_only + default validation --- rest_framework/serializers.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index d773902e89..7e84372def 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -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. @@ -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 = []