Explorar o código

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

Regression in 2d38eb0ab9f78d68c083a5b78b1eca39027b279a
Carlton Gibson %!s(int64=5) %!d(string=hai) anos
pai
achega
29c126bb34

+ 5 - 1
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,

+ 9 - 0
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
 ================
 

+ 4 - 0
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`).

+ 13 - 0
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.