Repository: encode/django-rest-framework

Issue:
Inheriting multiple Serializers gives unexpected order of overloading of fields
## Checklist

- [ x] I have verified that that issue exists against the `master` branch of Django REST framework.
- [ x] I have searched for similar issues in both open and closed tickets and cannot find a duplicate.
- [ x] This is not a usage question. (Those should be directed to the [discussion group](https://groups.google.com/forum/#!forum/django-rest-framework) instead.)
- [ x] This cannot be dealt with as a third party library. (We prefer new functionality to be [in the form of third party libraries](http://www.django-rest-framework.org/topics/third-party-resources/#about-third-party-packages) where possible.)
- [ x] I have reduced the issue to the simplest possible case.
- [ ] I have included a failing test as a pull request. (If you are unable to do so we can still accept the issue.)

## Steps to reproduce
inheriting a serializer from several other serializers. Some of them have fields with same name, and that name is not present localy in inheriting one
## Expected behavior
from those same-name fields, the first as in the order of inheritance should win
## Actual behavior
last one wins.

This is because in SerializerMetaclass._get_declared_fields():
a) bases is walked in reverse 
b) fields are prepended (i.e. reversing order once more)
c) repeating field-names are added just as any other; check is only against local-attrs
d) result list goes into ordereddict... hence last wins.

Easiest remedy is to reverse bases once more on entrance to this func... 
or to avoid breaking unknown amounts of code that *relies* on current behavior, keep it as is, and put big warning somewhere as documentation.

have fun


Discussion:
Hi. 

From the description, and from experience of working with inheritance and serialisers, the behaviour you describe sounds expected. 

I'm going to close this as such. If you can expand your "Steps to Reproduce" to include an actual (and minimal) code reproduce then I'm happy to re-open. (As it stands the issue is not actionable.) 

Thanks. 


Hi @carltongibson,

I've run into this before.  Here's a minimal example:
```
>>> from rest_framework import serializers
>>> class First(serializers.Serializer):
...     a = serializers.CharField()
>>> class Second(serializers.Serializer):
...     a = serializers.IntegerField()
>>> class MySerializer(First, Second):
...     pass
>>> MySerializer()
MySerializer():
    a = IntegerField()
```
Under normal Python MRO, you'd expect the `a` field on `MySerializer` to be a `CharField()` since that's what's on `First`.  However, we get an `IntegerField` due to the combination of reversing the bases and prepending the fields as described above.
It would have the intuitive behavior if https://github.com/encode/django-rest-framework/blob/0e4811e9ce30904bbd4f3aef6446d68be6c3a658/rest_framework/serializers.py#L304 iterated over the bases in the normal, rather than reverse, order.
Processing the mro in reverse is correct, since we want classes higher up in the mro to be overridden by those lower in the mro. The issue is the subsequent line, that prepends fields, effectively reversing order again (as pointed out by @svilendobrev). 

Reopening as this looks valid, and the above is enough of a test case to work with.

Resolving patch:
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -299,18 +299,22 @@ def _get_declared_fields(cls, bases, attrs):
                   if isinstance(obj, Field)]
         fields.sort(key=lambda x: x[1]._creation_counter)
 
-        # 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.
-        for base in reversed(bases):
-            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)
+        # Ensures a base class field doesn't override cls attrs, and maintains
+        # field precedence when inheriting multiple parents. e.g. if there is a
+        # class C(A, B), and A and B both define 'field', use 'field' from A.
+        known = set(attrs)
+
+        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):
         attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs)


Test patch:
diff --git a/tests/test_serializer.py b/tests/test_serializer.py
--- a/tests/test_serializer.py
+++ b/tests/test_serializer.py
@@ -682,3 +682,53 @@ class Grandchild(Child):
         assert len(Parent().get_fields()) == 2
         assert len(Child().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,
+        }