mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-26 03:23:59 +03:00
Fix serializer multiple inheritance bug (#6980)
* Expand declared filtering tests - Test declared filter ordering - Test multiple inheritance * Fix serializer multiple inheritance bug * Improve field order test to check for field types
This commit is contained in:
parent
7c5459626d
commit
b8c369c4cf
|
@ -298,18 +298,22 @@ class SerializerMetaclass(type):
|
||||||
if isinstance(obj, Field)]
|
if isinstance(obj, Field)]
|
||||||
fields.sort(key=lambda x: x[1]._creation_counter)
|
fields.sort(key=lambda x: x[1]._creation_counter)
|
||||||
|
|
||||||
# If this class is subclassing another Serializer, add that Serializer's
|
# Ensures a base class field doesn't override cls attrs, and maintains
|
||||||
# fields. Note that we loop over the bases in *reverse*. This is necessary
|
# field precedence when inheriting multiple parents. e.g. if there is a
|
||||||
# in order to maintain the correct order of fields.
|
# class C(A, B), and A and B both define 'field', use 'field' from A.
|
||||||
for base in reversed(bases):
|
known = set(attrs)
|
||||||
if hasattr(base, '_declared_fields'):
|
|
||||||
fields = [
|
|
||||||
(field_name, obj) for field_name, obj
|
|
||||||
in base._declared_fields.items()
|
|
||||||
if field_name not in attrs
|
|
||||||
] + fields
|
|
||||||
|
|
||||||
return OrderedDict(fields)
|
def visit(name):
|
||||||
|
known.add(name)
|
||||||
|
return name
|
||||||
|
|
||||||
|
base_fields = [
|
||||||
|
(visit(name), f)
|
||||||
|
for base in bases if hasattr(base, '_declared_fields')
|
||||||
|
for name, f in base._declared_fields.items() if name not in known
|
||||||
|
]
|
||||||
|
|
||||||
|
return OrderedDict(base_fields + 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)
|
||||||
|
|
|
@ -682,3 +682,53 @@ class TestDeclaredFieldInheritance:
|
||||||
assert len(Parent().get_fields()) == 2
|
assert len(Parent().get_fields()) == 2
|
||||||
assert len(Child().get_fields()) == 2
|
assert len(Child().get_fields()) == 2
|
||||||
assert len(Grandchild().get_fields()) == 2
|
assert len(Grandchild().get_fields()) == 2
|
||||||
|
|
||||||
|
def test_multiple_inheritance(self):
|
||||||
|
class A(serializers.Serializer):
|
||||||
|
field = serializers.CharField()
|
||||||
|
|
||||||
|
class B(serializers.Serializer):
|
||||||
|
field = serializers.IntegerField()
|
||||||
|
|
||||||
|
class TestSerializer(A, B):
|
||||||
|
pass
|
||||||
|
|
||||||
|
fields = {
|
||||||
|
name: type(f) for name, f
|
||||||
|
in TestSerializer()._declared_fields.items()
|
||||||
|
}
|
||||||
|
assert fields == {
|
||||||
|
'field': serializers.CharField,
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_field_ordering(self):
|
||||||
|
class Base(serializers.Serializer):
|
||||||
|
f1 = serializers.CharField()
|
||||||
|
f2 = serializers.CharField()
|
||||||
|
|
||||||
|
class A(Base):
|
||||||
|
f3 = serializers.IntegerField()
|
||||||
|
|
||||||
|
class B(serializers.Serializer):
|
||||||
|
f3 = serializers.CharField()
|
||||||
|
f4 = serializers.CharField()
|
||||||
|
|
||||||
|
class TestSerializer(A, B):
|
||||||
|
f2 = serializers.IntegerField()
|
||||||
|
f5 = serializers.CharField()
|
||||||
|
|
||||||
|
fields = {
|
||||||
|
name: type(f) for name, f
|
||||||
|
in TestSerializer()._declared_fields.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
# `IntegerField`s should be the 'winners' in field name conflicts
|
||||||
|
# - `TestSerializer.f2` should override `Base.F2`
|
||||||
|
# - `A.f3` should override `B.f3`
|
||||||
|
assert fields == {
|
||||||
|
'f1': serializers.CharField,
|
||||||
|
'f2': serializers.IntegerField,
|
||||||
|
'f3': serializers.IntegerField,
|
||||||
|
'f4': serializers.CharField,
|
||||||
|
'f5': serializers.CharField,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user