#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.