This commit is contained in:
Craig de Stigter 2016-01-18 20:20:35 +00:00
commit 7287b27a8d
4 changed files with 92 additions and 20 deletions

View File

@ -322,16 +322,6 @@ class Field(object):
Called when a field is added to the parent serializer instance. 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.field_name = field_name
self.parent = parent self.parent = parent

View File

@ -65,14 +65,6 @@ class RelatedField(Field):
self.queryset = kwargs.pop('queryset', self.queryset) self.queryset = kwargs.pop('queryset', self.queryset)
self.html_cutoff = kwargs.pop('html_cutoff', self.html_cutoff) self.html_cutoff = kwargs.pop('html_cutoff', self.html_cutoff)
self.html_cutoff_text = kwargs.pop('html_cutoff_text', self.html_cutoff_text) 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('many', None)
kwargs.pop('allow_empty', None) kwargs.pop('allow_empty', None)
super(RelatedField, self).__init__(**kwargs) super(RelatedField, self).__init__(**kwargs)

View File

@ -280,11 +280,19 @@ class SerializerMetaclass(type):
# If this class is subclassing another Serializer, add that Serializer's # If this class is subclassing another Serializer, add that Serializer's
# fields. Note that we loop over the bases in *reverse*. This is necessary # fields. Note that we loop over the bases in *reverse*. This is necessary
# in order to maintain the correct order of fields. # in order to maintain the correct order of fields.
all_fields = OrderedDict()
for base in reversed(bases): for base in reversed(bases):
if hasattr(base, '_declared_fields'): 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): def __new__(cls, name, bases, attrs):
attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs) attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs)

View File

@ -309,3 +309,85 @@ class TestCacheSerializerData:
pickled = pickle.dumps(serializer.data) pickled = pickle.dumps(serializer.data)
data = pickle.loads(pickled) data = pickle.loads(pickled)
assert data == {'field1': 'a', 'field2': 'b'} 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)