Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow overriding of fields in subclassed serializers. #1053

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions rest_framework/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,29 @@ def _get_declared_fields(bases, attrs):
# 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.
# Note: The 'seen' dict here ensures that the fields from the 'first' base
# take precedence over fields from later bases.
# (otherwise SortedDict will squash the first-base field and
# use the field from a later base instead.)
seen = set()
base_fields_map = {}
base_fields_order = []
for base in bases[::-1]:
if hasattr(base, 'base_fields'):
fields = list(base.base_fields.items()) + fields

return SortedDict(fields)
for name, field in base.base_fields.items():
base_fields_map[name] = field
base_fields_order = list(base.base_fields.keys()) + base_fields_order

seen = set()
base_fields = []
for name in base_fields_order:
if name not in seen:
base_fields.append((name, base_fields_map[name]))
seen.add(name)

# if there are fields in both base_fields and fields, SortedDict
# uses the *last* one defined. So fields needs to go last.
return SortedDict(base_fields + fields)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still struggling to review. C'mon brain. :-/



class SerializerMetaclass(type):
Expand Down
69 changes: 69 additions & 0 deletions rest_framework/tests/test_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1643,3 +1643,72 @@ def test_serializer_supports_slug_many_relationships(self):
serializer = SimpleSlugSourceModelSerializer(data={'text': 'foo', 'targets': [1, 2]})
self.assertTrue(serializer.is_valid())
self.assertEqual(serializer.data, {'text': 'foo', 'targets': [1, 2]})


### Regression test for #1053

class OverriddenFieldsBase1(serializers.Serializer):
a_field = serializers.CharField()


class OverriddenFieldsBase2(serializers.Serializer):
a_field = serializers.IntegerField()


class OverriddenFieldsWithSingleBase(OverriddenFieldsBase1):
a_field = serializers.FloatField()


class OverriddenFieldsMultipleBases1(OverriddenFieldsBase1, OverriddenFieldsBase2):
# first base takes precedence; a_field should be a CharField.
pass


class OverriddenFieldsMultipleBases2(OverriddenFieldsBase2, OverriddenFieldsBase1):
# first base takes precedence; a_field should be a IntegerField.
pass


class OverriddenFieldsMultipleBasesOverridden(OverriddenFieldsBase1, OverriddenFieldsBase2):
a_field = serializers.FloatField()


class SerializerSupportsOverriddenFields(TestCase):
def test_base_fields_unchanged(self):
self.assertIsInstance(
OverriddenFieldsBase1.base_fields['a_field'],
serializers.CharField,
)
s = OverriddenFieldsBase1()
self.assertIsInstance(s.fields['a_field'], serializers.CharField)

def test_overridden_fields_single_base(self):
self.assertIsInstance(
OverriddenFieldsWithSingleBase.base_fields['a_field'],
serializers.FloatField,
)
s = OverriddenFieldsWithSingleBase()
self.assertIsInstance(s.fields['a_field'], serializers.FloatField)

def test_overridden_fields_multiple_bases(self):
self.assertIsInstance(
OverriddenFieldsMultipleBases1.base_fields['a_field'],
serializers.CharField,
)
s = OverriddenFieldsMultipleBases1()
self.assertIsInstance(s.fields['a_field'], serializers.CharField)

self.assertIsInstance(
OverriddenFieldsMultipleBases2.base_fields['a_field'],
serializers.IntegerField,
)
s = OverriddenFieldsMultipleBases2()
self.assertIsInstance(s.fields['a_field'], serializers.IntegerField)

def test_overridden_fields_multiple_bases_overridden(self):
self.assertIsInstance(
OverriddenFieldsMultipleBasesOverridden.base_fields['a_field'],
serializers.FloatField,
)
s = OverriddenFieldsMultipleBasesOverridden()
self.assertIsInstance(s.fields['a_field'], serializers.FloatField)