diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index fd0a295..3eea553 100644
|
a
|
b
|
class Field(object):
|
| 234 | 234 | def contribute_to_class(self, cls, name): |
| 235 | 235 | self.set_attributes_from_name(name) |
| 236 | 236 | self.model = cls |
| | 237 | if not hasattr(self, "_declared_on_class"): |
| | 238 | self._declared_on_class = cls |
| | 239 | if not hasattr(self, "_first_concrete_class") and not cls._meta.abstract: |
| | 240 | self._first_concrete_class = cls |
| 237 | 241 | cls._meta.add_field(self) |
| 238 | | if self.choices: |
| 239 | | setattr(cls, 'get_%s_display' % self.name, curry(cls._get_FIELD_display, field=self)) |
| 240 | 242 | |
| 241 | 243 | def get_attname(self): |
| 242 | 244 | return self.name |
diff --git a/django/db/models/options.py b/django/db/models/options.py
index 10617dc..fe16663 100644
|
a
|
b
|
from django.db.models.loading import get_models, app_cache_ready
|
| 10 | 10 | from django.utils.translation import activate, deactivate_all, get_language, string_concat |
| 11 | 11 | from django.utils.encoding import force_unicode, smart_str |
| 12 | 12 | from django.utils.datastructures import SortedDict |
| | 13 | from django.utils.functional import curry |
| 13 | 14 | |
| 14 | 15 | try: |
| 15 | 16 | all |
| … |
… |
class Options(object):
|
| 64 | 65 | from django.db.backends.util import truncate_name |
| 65 | 66 | |
| 66 | 67 | cls._meta = self |
| | 68 | self.model = cls |
| 67 | 69 | self.installed = re.sub('\.models$', '', cls.__module__) in settings.INSTALLED_APPS |
| 68 | 70 | # First, construct the default values for these options. |
| 69 | 71 | self.object_name = cls.__name__ |
| … |
… |
class Options(object):
|
| 158 | 160 | # Insert the given field in the order in which it was created, using |
| 159 | 161 | # the "creation_counter" attribute of the field. |
| 160 | 162 | # Move many-to-many related fields from self.fields into |
| 161 | | # self.many_to_many. |
| | 163 | # self.many_to_many. Ignore duplicate fields inherited from a |
| | 164 | # single abstract base class. |
| | 165 | try: |
| | 166 | f = self.get_field(field.name) |
| | 167 | except FieldDoesNotExist: |
| | 168 | pass |
| | 169 | else: |
| | 170 | if f._declared_on_class == field._declared_on_class and getattr(f, '_first_concrete_class', None) == getattr(field, '_first_concrete_class', None): |
| | 171 | return |
| | 172 | |
| 162 | 173 | if field.rel and isinstance(field.rel, ManyToManyRel): |
| 163 | 174 | self.local_many_to_many.insert(bisect(self.local_many_to_many, field), field) |
| 164 | 175 | if hasattr(self, '_m2m_cache'): |
| … |
… |
class Options(object):
|
| 173 | 184 | if hasattr(self, '_name_map'): |
| 174 | 185 | del self._name_map |
| 175 | 186 | |
| | 187 | if field.choices: |
| | 188 | setattr(self.model, 'get_%s_display' % field.name, curry(self.model._get_FIELD_display, field=field)) |
| | 189 | |
| 176 | 190 | def add_virtual_field(self, field): |
| 177 | 191 | self.virtual_fields.append(field) |
| 178 | 192 | |
diff --git a/tests/modeltests/model_inheritance/models.py b/tests/modeltests/model_inheritance/models.py
index a0fed8a..bbdbd37 100644
|
a
|
b
|
class Comment(Attachment):
|
| 64 | 64 | class Link(Attachment): |
| 65 | 65 | url = models.URLField() |
| 66 | 66 | |
| | 67 | class OrangeAttachment(Attachment): |
| | 68 | class Meta: |
| | 69 | abstract = True |
| | 70 | |
| | 71 | class BananaAttachment(Attachment): |
| | 72 | class Meta: |
| | 73 | abstract = True |
| | 74 | |
| 67 | 75 | # |
| 68 | 76 | # Multi-table inheritance |
| 69 | 77 | # |
diff --git a/tests/modeltests/model_inheritance/tests.py b/tests/modeltests/model_inheritance/tests.py
index 334297a..bdcc28d 100644
|
a
|
b
|
from django.core.exceptions import FieldError
|
| 4 | 4 | from django.test import TestCase |
| 5 | 5 | |
| 6 | 6 | from models import (Chef, CommonInfo, ItalianRestaurant, ParkingLot, Place, |
| 7 | | Post, Restaurant, Student, StudentWorker, Supplier, Worker, MixinModel) |
| | 7 | Post, Restaurant, Student, StudentWorker, Supplier, Worker, MixinModel, |
| | 8 | OrangeAttachment, BananaAttachment) |
| 8 | 9 | |
| 9 | 10 | |
| 10 | 11 | class ModelInheritanceTests(TestCase): |
| … |
… |
class ModelInheritanceTests(TestCase):
|
| 69 | 70 | StudentWorker.objects.get, pk__lt=sw2.pk + 100 |
| 70 | 71 | ) |
| 71 | 72 | |
| | 73 | def test_multiple_abstract_inheritance(self): |
| | 74 | # Define class here to avoid model validation errors while running tests. |
| | 75 | class SmoothieAttachment(OrangeAttachment, BananaAttachment): |
| | 76 | class Meta: |
| | 77 | app_label = 'model_inheritance' |
| | 78 | |
| | 79 | # There should be no duplicate field names. |
| | 80 | field_names = [field.name for field in SmoothieAttachment._meta.fields] |
| | 81 | self.assertEqual(field_names.sort(), list(set(field_names)).sort()) |
| | 82 | |
| 72 | 83 | def test_multiple_table(self): |
| 73 | 84 | post = Post.objects.create(title="Lorem Ipsum") |
| 74 | 85 | # The Post model has distinct accessors for the Comment and Link models. |