Code

Ticket #15279: ticket15279_r15553.diff

File ticket15279_r15553.diff, 4.8 KB (added by melinath, 3 years ago)
Line 
1diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
2index fd0a295..3eea553 100644
3--- a/django/db/models/fields/__init__.py
4+++ b/django/db/models/fields/__init__.py
5@@ -234,9 +234,11 @@ class Field(object):
6     def contribute_to_class(self, cls, name):
7         self.set_attributes_from_name(name)
8         self.model = cls
9+        if not hasattr(self, "_declared_on_class"):
10+            self._declared_on_class = cls
11+        if not hasattr(self, "_first_concrete_class") and not cls._meta.abstract:
12+            self._first_concrete_class = cls
13         cls._meta.add_field(self)
14-        if self.choices:
15-            setattr(cls, 'get_%s_display' % self.name, curry(cls._get_FIELD_display, field=self))
16 
17     def get_attname(self):
18         return self.name
19diff --git a/django/db/models/options.py b/django/db/models/options.py
20index 10617dc..fe16663 100644
21--- a/django/db/models/options.py
22+++ b/django/db/models/options.py
23@@ -10,6 +10,7 @@ from django.db.models.loading import get_models, app_cache_ready
24 from django.utils.translation import activate, deactivate_all, get_language, string_concat
25 from django.utils.encoding import force_unicode, smart_str
26 from django.utils.datastructures import SortedDict
27+from django.utils.functional import curry
28 
29 try:
30     all
31@@ -64,6 +65,7 @@ class Options(object):
32         from django.db.backends.util import truncate_name
33 
34         cls._meta = self
35+        self.model = cls
36         self.installed = re.sub('\.models$', '', cls.__module__) in settings.INSTALLED_APPS
37         # First, construct the default values for these options.
38         self.object_name = cls.__name__
39@@ -158,7 +160,16 @@ class Options(object):
40         # Insert the given field in the order in which it was created, using
41         # the "creation_counter" attribute of the field.
42         # Move many-to-many related fields from self.fields into
43-        # self.many_to_many.
44+        # self.many_to_many. Ignore duplicate fields inherited from a
45+        # single abstract base class.
46+        try:
47+            f = self.get_field(field.name)
48+        except FieldDoesNotExist:
49+            pass
50+        else:
51+            if f._declared_on_class == field._declared_on_class and getattr(f, '_first_concrete_class', None) == getattr(field, '_first_concrete_class', None):
52+                return
53+
54         if field.rel and isinstance(field.rel, ManyToManyRel):
55             self.local_many_to_many.insert(bisect(self.local_many_to_many, field), field)
56             if hasattr(self, '_m2m_cache'):
57@@ -173,6 +184,9 @@ class Options(object):
58         if hasattr(self, '_name_map'):
59             del self._name_map
60 
61+        if field.choices:
62+            setattr(self.model, 'get_%s_display' % field.name, curry(self.model._get_FIELD_display, field=field))
63+
64     def add_virtual_field(self, field):
65         self.virtual_fields.append(field)
66 
67diff --git a/tests/modeltests/model_inheritance/models.py b/tests/modeltests/model_inheritance/models.py
68index a0fed8a..bbdbd37 100644
69--- a/tests/modeltests/model_inheritance/models.py
70+++ b/tests/modeltests/model_inheritance/models.py
71@@ -64,6 +64,14 @@ class Comment(Attachment):
72 class Link(Attachment):
73     url = models.URLField()
74 
75+class OrangeAttachment(Attachment):
76+    class Meta:
77+        abstract = True
78+
79+class BananaAttachment(Attachment):
80+    class Meta:
81+        abstract = True
82+
83 #
84 # Multi-table inheritance
85 #
86diff --git a/tests/modeltests/model_inheritance/tests.py b/tests/modeltests/model_inheritance/tests.py
87index 334297a..bdcc28d 100644
88--- a/tests/modeltests/model_inheritance/tests.py
89+++ b/tests/modeltests/model_inheritance/tests.py
90@@ -4,7 +4,8 @@ from django.core.exceptions import FieldError
91 from django.test import TestCase
92 
93 from models import (Chef, CommonInfo, ItalianRestaurant, ParkingLot, Place,
94-    Post, Restaurant, Student, StudentWorker, Supplier, Worker, MixinModel)
95+    Post, Restaurant, Student, StudentWorker, Supplier, Worker, MixinModel,
96+    OrangeAttachment, BananaAttachment)
97 
98 
99 class ModelInheritanceTests(TestCase):
100@@ -69,6 +70,16 @@ class ModelInheritanceTests(TestCase):
101             StudentWorker.objects.get, pk__lt=sw2.pk + 100
102         )
103 
104+    def test_multiple_abstract_inheritance(self):
105+        # Define class here to avoid model validation errors while running tests.
106+        class SmoothieAttachment(OrangeAttachment, BananaAttachment):
107+            class Meta:
108+                app_label = 'model_inheritance'
109+
110+        # There should be no duplicate field names.
111+        field_names = [field.name for field in SmoothieAttachment._meta.fields]
112+        self.assertEqual(field_names.sort(), list(set(field_names)).sort())
113+
114     def test_multiple_table(self):
115         post = Post.objects.create(title="Lorem Ipsum")
116         # The Post model has distinct accessors for the Comment and Link models.