summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlton Gibson <carlton.gibson@noumenal.es>2020-01-07 11:52:09 +0100
committerCarlton Gibson <carlton@noumenal.es>2020-01-15 15:35:22 +0100
commit29c126bb349526b5f1cd78facbe9f25906f18563 (patch)
tree1619db156a90607b54bce848c676867a67c86646
parentd202846ced2f58d7a34ad80bfe2bde8a542a70b9 (diff)
Fixed #31124 -- Fixed setting of get_FOO_display() when overriding inherited choices.
Regression in 2d38eb0ab9f78d68c083a5b78b1eca39027b279a
-rw-r--r--django/db/models/fields/__init__.py6
-rw-r--r--docs/ref/models/instances.txt9
-rw-r--r--docs/releases/3.0.3.txt4
-rw-r--r--tests/model_fields/tests.py13
4 files changed, 31 insertions, 1 deletions
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 1a55d3d417..6bd95f542c 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -764,7 +764,11 @@ class Field(RegisterLookupMixin):
if not getattr(cls, self.attname, None):
setattr(cls, self.attname, self.descriptor_class(self))
if self.choices is not None:
- if not hasattr(cls, 'get_%s_display' % self.name):
+ # Don't override a get_FOO_display() method defined explicitly on
+ # this class, but don't check methods derived from inheritance, to
+ # allow overriding inherited choices. For more complex inheritance
+ # structures users should override contribute_to_class().
+ if 'get_%s_display' % self.name not in cls.__dict__:
setattr(
cls,
'get_%s_display' % self.name,
diff --git a/docs/ref/models/instances.txt b/docs/ref/models/instances.txt
index 1524ad2fcd..6adcb979ba 100644
--- a/docs/ref/models/instances.txt
+++ b/docs/ref/models/instances.txt
@@ -834,6 +834,15 @@ Note that in the case of identical date values, these methods will use the
primary key as a tie-breaker. This guarantees that no records are skipped or
duplicated. That also means you cannot use those methods on unsaved objects.
+.. admonition:: Overriding extra instance methods
+
+ In most cases overriding or inheriting ``get_FOO_display()``,
+ ``get_next_by_FOO()``, and ``get_previous_by_FOO()` should work as
+ expected. Since they are added by the metaclass however, it is not
+ practical to account for all possible inheritance structures. In more
+ complex cases you should override ``Field.contribute_to_class()`` to set
+ the methods you need.
+
Other attributes
================
diff --git a/docs/releases/3.0.3.txt b/docs/releases/3.0.3.txt
index c2cac9203d..2726e2d3ab 100644
--- a/docs/releases/3.0.3.txt
+++ b/docs/releases/3.0.3.txt
@@ -31,3 +31,7 @@ Bugfixes
:class:`~django.contrib.postgres.aggregates.ArrayAgg` and
:class:`~django.contrib.postgres.aggregates.StringAgg` with ``filter``
argument when used in a ``Subquery`` (:ticket:`31097`).
+
+* Fixed a regression in Django 2.2.7 that caused
+ :meth:`~django.db.models.Model.get_FOO_display` to work incorrectly when
+ overriding inherited choices (:ticket:`31124`).
diff --git a/tests/model_fields/tests.py b/tests/model_fields/tests.py
index a3b805409c..b97c99d42d 100644
--- a/tests/model_fields/tests.py
+++ b/tests/model_fields/tests.py
@@ -178,6 +178,19 @@ class GetFieldDisplayTests(SimpleTestCase):
f = FooBar(foo_bar=1)
self.assertEqual(f.get_foo_bar_display(), 'something')
+ def test_overriding_inherited_FIELD_display(self):
+ class Base(models.Model):
+ foo = models.CharField(max_length=254, choices=[('A', 'Base A')])
+
+ class Meta:
+ abstract = True
+
+ class Child(Base):
+ foo = models.CharField(max_length=254, choices=[('A', 'Child A'), ('B', 'Child B')])
+
+ self.assertEqual(Child(foo='A').get_foo_display(), 'Child A')
+ self.assertEqual(Child(foo='B').get_foo_display(), 'Child B')
+
def test_iterator_choices(self):
"""
get_choices() works with Iterators.