diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 038a4d6b99..48d9723ec3 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -532,7 +532,7 @@ This option should be a list or tuple of field names, and is declared as follows fields = ('id', 'account_name', 'users', 'created') read_only_fields = ('account_name',) -Model fields which have `editable=False` set, and `AutoField` fields will be set to read-only by default, and do not need to be added to the `read_only_fields` option. +Model fields which have `editable=False` set, and `AutoField` fields will be set to read-only by default, and do not need to be added to the `read_only_fields` option. Having a field listed in `read_only_fields` and declared on the class will raise an exception. --- @@ -570,6 +570,8 @@ This option is a dictionary, mapping field names to a dictionary of keyword argu user.save() return user +Having arguments for a field in `extra_kwargs` for a field declared on the class will raise an exception. + ## Relational fields When serializing model instances, there are a number of different ways you might choose to represent relationships. The default representation for `ModelSerializer` is to use the primary keys of the related instances. diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index 3fcc85c3b7..7883bd80d7 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -947,6 +947,11 @@ def get_fields(self): # Determine any extra field arguments and hidden fields that # should be included extra_kwargs = self.get_extra_kwargs() + for field in extra_kwargs: + assert field not in declared_fields, ( + "Field {} is declared on the class and also present in " + "extra_kwargs or read_only_fields".format(field) + ) extra_kwargs, hidden_fields = self.get_uniqueness_extra_kwargs( field_names, declared_fields, extra_kwargs ) @@ -1321,12 +1326,11 @@ def get_uniqueness_extra_kwargs(self, field_names, declared_fields, extra_kwargs # add in a hidden field that populates it. hidden_fields[unique_constraint_name] = HiddenField(default=default) - # Update `extra_kwargs` with any new options. + # Update `extra_kwargs` with any new options but don't overwrite old values. for key, value in uniqueness_extra_kwargs.items(): if key in extra_kwargs: - extra_kwargs[key].update(value) - else: - extra_kwargs[key] = value + value.update(extra_kwargs[key]) + extra_kwargs[key] = value return extra_kwargs, hidden_fields diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 096cbc8d64..b812a67918 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -933,8 +933,8 @@ class TestSerializer(serializers.ModelSerializer): class Meta: model = OneFieldModel - read_only_fields = ('char_field', 'non_model_field') - fields = read_only_fields + read_only_fields = ('char_field',) + fields = read_only_fields + ('non_model_field', ) extra_kwargs = {} class ChildSerializer(TestSerializer): @@ -955,3 +955,44 @@ class Meta(TestSerializer.Meta): self.assertEqual(unicode_repr(ChildSerializer()), child_expected) self.assertEqual(unicode_repr(TestSerializer()), test_expected) self.assertEqual(unicode_repr(ChildSerializer()), child_expected) + + +class TestDeclaredFieldsConflict(TestCase): + def test_extra_kwargs_conflict(self): + class TestSerializer(serializers.ModelSerializer): + some_field = serializers.CharField() + + class Meta: + model = OneFieldModel + extra_kwargs = {'some_field': {'read_only': True}} + with self.assertRaises(AssertionError): + TestSerializer().get_fields() + + def test_read_only_fields_conflict(self): + class TestSerializer(serializers.ModelSerializer): + some_field = serializers.CharField() + + class Meta: + model = OneFieldModel + read_only_fields = ('some_field', ) + with self.assertRaises(AssertionError): + TestSerializer().get_fields() + + +class TestUniquenessOverride(TestCase): + def test_required_not_overwritten(self): + class TestModel(models.Model): + field_1 = models.IntegerField(null=True) + field_2 = models.IntegerField() + + class Meta: + unique_together = (('field_1', 'field_2'),) + + class TestSerializer(serializers.ModelSerializer): + class Meta: + model = TestModel + extra_kwargs = {'field_1': {'required': False}} + + fields = TestSerializer().fields + self.assertFalse(fields['field_1'].required) + self.assertTrue(fields['field_2'].required)
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: