diff --git a/rest_framework/fields.py b/rest_framework/fields.py index a2b0f3bf1..b9b3ca443 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -322,16 +322,6 @@ class Field(object): Called when a field is added to the parent serializer instance. """ - # In order to enforce a consistent style, we error if a redundant - # 'source' argument has been used. For example: - # my_field = serializer.CharField(source='my_field') - assert self.source != field_name, ( - "It is redundant to specify `source='%s'` on field '%s' in " - "serializer '%s', because it is the same as the field name. " - "Remove the `source` keyword argument." % - (field_name, self.__class__.__name__, parent.__class__.__name__) - ) - self.field_name = field_name self.parent = parent diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 744823c48..083b56bf8 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -65,14 +65,6 @@ class RelatedField(Field): self.queryset = kwargs.pop('queryset', self.queryset) self.html_cutoff = kwargs.pop('html_cutoff', self.html_cutoff) self.html_cutoff_text = kwargs.pop('html_cutoff_text', self.html_cutoff_text) - assert self.queryset is not None or kwargs.get('read_only', None), ( - 'Relational field must provide a `queryset` argument, ' - 'or set read_only=`True`.' - ) - assert not (self.queryset is not None and kwargs.get('read_only', None)), ( - 'Relational fields should not provide a `queryset` argument, ' - 'when setting read_only=`True`.' - ) kwargs.pop('many', None) kwargs.pop('allow_empty', None) super(RelatedField, self).__init__(**kwargs) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index f7b1e1b55..3920afc24 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -280,11 +280,19 @@ class SerializerMetaclass(type): # If this class is subclassing another Serializer, add that Serializer's # fields. Note that we loop over the bases in *reverse*. This is necessary # in order to maintain the correct order of fields. + all_fields = OrderedDict() for base in reversed(bases): if hasattr(base, '_declared_fields'): - fields = list(base._declared_fields.items()) + fields + for name, field in base._declared_fields.items(): + if name in all_fields: + # Throw away old ordering, then replace with new one + all_fields.pop(name) + all_fields[name] = field - return OrderedDict(fields) + # if there are fields in both base_fields and fields, OrderedDict + # uses the *last* one defined. So fields needs to go last. + all_fields.update(OrderedDict(fields)) + return all_fields def __new__(cls, name, bases, attrs): attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 741c6ab17..65dd4aca2 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -309,3 +309,85 @@ class TestCacheSerializerData: pickled = pickle.dumps(serializer.data) data = pickle.loads(pickled) assert data == {'field1': 'a', 'field2': 'b'} + + +class TestSerializerSupportsOverriddenFields: + def setup(self): + class Base1(serializers.Serializer): + a_field = serializers.CharField() + self.Base1 = Base1 + + class Base2(serializers.Serializer): + a_field = serializers.IntegerField() + self.Base2 = Base2 + + def test_base_fields_unchanged(self): + """ + Overriding a field in a subclassed serializer shouldn't change the + field on the superclass + """ + class OverriddenFields(self.Base1): + a_field = serializers.FloatField() + + assert isinstance( + self.Base1._declared_fields['a_field'], + serializers.CharField, + ) + s = self.Base1() + assert isinstance(s.fields['a_field'], serializers.CharField) + + def test_overridden_fields_single_base(self): + """ + Subclassing a serializer and overriding a field should mean the field + on the subclass wins. + """ + class OverriddenFieldsWithSingleBase(self.Base1): + a_field = serializers.FloatField() + + assert isinstance( + OverriddenFieldsWithSingleBase._declared_fields['a_field'], + serializers.FloatField, + ) + s = OverriddenFieldsWithSingleBase() + assert isinstance(s.fields['a_field'], serializers.FloatField) + + def test_overridden_fields_multiple_bases(self): + """ + For serializers with multiple bases, the field on the first base wins + (as per normal python method resolution order) + """ + class OverriddenFieldsMultipleBases1(self.Base1, self.Base2): + # first base takes precedence; a_field should be a CharField. + pass + + assert isinstance( + OverriddenFieldsMultipleBases1._declared_fields['a_field'], + serializers.CharField, + ) + s = OverriddenFieldsMultipleBases1() + assert isinstance(s.fields['a_field'], serializers.CharField) + + class OverriddenFieldsMultipleBases2(self.Base2, self.Base1): + # first base takes precedence; a_field should be a IntegerField. + pass + + assert isinstance( + OverriddenFieldsMultipleBases2._declared_fields['a_field'], + serializers.IntegerField, + ) + s = OverriddenFieldsMultipleBases2() + assert isinstance(s.fields['a_field'], serializers.IntegerField) + + def test_overridden_fields_multiple_bases_overridden(self): + """ + For serializers with multiple bases, locally defined fields still win. + """ + class OverriddenFieldsMultipleBasesOverridden(self.Base1, self.Base2): + a_field = serializers.FloatField() + + assert isinstance( + OverriddenFieldsMultipleBasesOverridden._declared_fields['a_field'], + serializers.FloatField, + ) + s = OverriddenFieldsMultipleBasesOverridden() + assert isinstance(s.fields['a_field'], serializers.FloatField)