Fixed #31124 -- Fixed setting of get_FOO_display() when overriding inherited choices.

Regression in 2d38eb0ab9
This commit is contained in:
Carlton Gibson 2020-01-07 11:52:09 +01:00 committed by Carlton Gibson
parent d202846ced
commit 29c126bb34
4 changed files with 31 additions and 1 deletions

View File

@ -764,7 +764,11 @@ class Field(RegisterLookupMixin):
if not getattr(cls, self.attname, None): if not getattr(cls, self.attname, None):
setattr(cls, self.attname, self.descriptor_class(self)) setattr(cls, self.attname, self.descriptor_class(self))
if self.choices is not None: 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( setattr(
cls, cls,
'get_%s_display' % self.name, 'get_%s_display' % self.name,

View File

@ -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 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. 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 Other attributes
================ ================

View File

@ -31,3 +31,7 @@ Bugfixes
:class:`~django.contrib.postgres.aggregates.ArrayAgg` and :class:`~django.contrib.postgres.aggregates.ArrayAgg` and
:class:`~django.contrib.postgres.aggregates.StringAgg` with ``filter`` :class:`~django.contrib.postgres.aggregates.StringAgg` with ``filter``
argument when used in a ``Subquery`` (:ticket:`31097`). 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`).

View File

@ -178,6 +178,19 @@ class GetFieldDisplayTests(SimpleTestCase):
f = FooBar(foo_bar=1) f = FooBar(foo_bar=1)
self.assertEqual(f.get_foo_bar_display(), 'something') 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): def test_iterator_choices(self):
""" """
get_choices() works with Iterators. get_choices() works with Iterators.