#30931: Cannot override get_FOO_display() in Django 2.2+.
-------------------------------------+-------------------------------------
     Reporter:  Jim Ouwerkerk        |                    Owner:  Carlton
         Type:                       |  Gibson
  Cleanup/optimization               |                   Status:  assigned
    Component:  Documentation        |                  Version:  2.2
     Severity:  Normal               |               Resolution:
     Keywords:                       |             Triage Stage:  Accepted
    Has patch:  1                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------

Comment (by Carlton Gibson):

 > They pass on 2.1 too...

 Err. Can I ask you to check again please. We're obviously seeing something
 different.

 {{{
 $ git show --name-only
 commit 522af9d6737550ef035a173c08a8276028b68917 (HEAD -> stable/2.1.x,
 origin/stable/2.1.x)
 ...
 (django) ~/Documents/Django-Stack/django/tests (stable/2.1.x)$ git diff
 diff --git a/django/db/models/fields/__init__.py
 b/django/db/models/fields/__init__.py
 index 50d22bef0c..38aed6c818 100644
 --- a/django/db/models/fields/__init__.py
 +++ b/django/db/models/fields/__init__.py
 @@ -744,8 +744,9 @@ class Field(RegisterLookupMixin):
              if not getattr(cls, self.attname, None):
                  setattr(cls, self.attname,
 DeferredAttribute(self.attname))
          if self.choices:
 -            setattr(cls, 'get_%s_display' % self.name,
 -                    partialmethod(cls._get_FIELD_display, field=self))
 +            if not hasattr(cls, 'get_%s_display' % self.name):
 +                setattr(cls, 'get_%s_display' % self.name,
 +                        partialmethod(cls._get_FIELD_display,
 field=self))

      def get_filter_kwargs_for_object(self, obj):
          """
 diff --git a/tests/model_fields/tests.py b/tests/model_fields/tests.py
 index 45f61a0034..31766a2d5c 100644
 --- a/tests/model_fields/tests.py
 +++ b/tests/model_fields/tests.py
 @@ -157,3 +157,25 @@ class GetChoicesTests(SimpleTestCase):
          lazy_func = lazy(lambda x: 0 / 0, int)  # raises
 ZeroDivisionError if evaluated.
          f = models.CharField(choices=[(lazy_func('group'), (('a', 'A'),
 ('b', 'B')))])
          self.assertEqual(f.get_choices(include_blank=True)[0], ('',
 '---------'))
 +
 +    def test_overriding_FIELD_display(self):
 +        class FooBar(models.Model):
 +            foo_bar = models.CharField(choices=[(1, 'foo'), (2, 'bar')])
 +
 +            def _get_FIELD_display(self, field):
 +                if field.attname == 'foo_bar':
 +                    return "something"
 +                return super()._get_FIELD_display(field)
 +
 +        f = FooBar(foo_bar=1)
 +        self.assertEqual(f.get_foo_bar_display(), "something")
 +
 +    def test_overriding_FIELD_display_2(self):
 +        class FooBar2(models.Model):
 +            def get_foo_bar_display(self):
 +                return 'something'
 +
 +            foo_bar = models.CharField(choices=[(1, 'foo'), (2, 'bar')])
 +
 +        f = FooBar2(foo_bar=1)
 +        self.assertEqual(f.get_foo_bar_display(), 'something')
 (django) ~/Documents/Django-Stack/django/tests (stable/2.1.x)$
 ./runtests.py model_fields.tests.GetChoicesTests
 Testing against Django installed in '/Users/carlton/Documents/Django-
 Stack/django/django' with up to 4 processes
 Creating test database for alias 'default'...
 Creating test database for alias 'other'...
 System check identified no issues (0 silenced).
 ...F.
 ======================================================================
 FAIL: test_overriding_FIELD_display (model_fields.tests.GetChoicesTests)
 ----------------------------------------------------------------------
 Traceback (most recent call last):
   File "/Users/carlton/Documents/Django-
 Stack/django/tests/model_fields/tests.py", line 171, in
 test_overriding_FIELD_display
     self.assertEqual(f.get_foo_bar_display(), "something")
 AssertionError: 'foo' != 'something'
 - foo
 + something


 ----------------------------------------------------------------------
 Ran 5 tests in 0.004s

 FAILED (failures=1)
 Destroying test database for alias 'default'...
 Destroying test database for alias 'other'...
 }}}

 It's relevant precisely because it's a change in behaviour. ("Regression
 from previous version...")

 > I don't think that overriding non-public methods could be considered as
 the correct way of customization.

 OK. In this case I disagree. (That's allowed.) I think it's a rare use-
 case, worth documenting, but not worth adding a code path for.

 > It already does...

 Yes, but more isn't better here.

 (You're welcome to think different.)

 @Jim: Yes, I hear your sentiment, but, for me, here you're dealing with
 special methods added by the metaclass. It's not a normal case of
 subclassing.

 I would much rather state, plainly, the ''vanilla'' way of achieving the
 desired result, even if pointing to an otherwise private method, than add
 code to ''magic'' the same thing. Every hook we add back from `Field`
 makes the code here marginally more difficult to maintain and adjust.
 Again for me, this is niche use-case that doesn't merit that addition.

 I'd hope that at least make sense.

-- 
Ticket URL: <https://code.djangoproject.com/ticket/30931#comment:15>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-updates+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/070.f71d8f97859507ca94d9ff0b84864d58%40djangoproject.com.

Reply via email to