11

In general, I'm not familiar with python's way of overriding methods and using super().

question is: can I override get_FOO_display()?

class A(models.Model):
   unit = models.IntegerField(choices=something)

   def get_unit_display(self, value):
     ... use super(A, self).get_unit_display() 

I want to override get_FOO_display() because I want to pluralize my display.

But super(A, self).get_unit_display() doesn't work.

Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
dtc
  • 1,774
  • 2
  • 21
  • 44

5 Answers5

10

Normally you would just override a method as you have shown. But the trick here is that the get_FOO_display method is not present on the superclass, so calling the super method will do nothing at all. The method is added dynamically by the field class when it is added to the model by the metaclass - see the source here (EDIT: outdated link as permalink).

One thing you could do is define a custom Field subclass for your unit field, and override contribute_to_class so that it constructs the method you want. It's a bit tricky unfortunately.

(I don't understand your second question. What exactly are you asking?)

Bruno A.
  • 1,765
  • 16
  • 17
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • Sorry, ignore my poorly explained 2nd question. I got it figured out. – dtc Dec 18 '12 at 18:51
  • I don't know how it was back in 2012, but nowadays you can just add your custom `get_foo_display` method in your model definition (without reference to super) and it will be available during template rendering. Don't forget to use the `|safe tag` as you'll probably want to render html. – May.D Nov 20 '18 at 08:55
1

Now in Django > 2.2.7:

Restored the ability to override get_FOO_display() (#30931).

You can override:


    class FooBar(models.Model):
        foo_bar = models.CharField(_("foo"),  choices=[(1, 'foo'), (2, 'bar')])
    
    
   
        def get_foo_bar_display(self):
            return "something"

Omid Raha
  • 9,862
  • 1
  • 60
  • 64
0

You could do it this way:

  1. Override the Django IntegerField to make a copy of your get_FOO_display function:

    class MyIntegerField(models.IntegerField):
        def contribute_to_class(self, cls, name, private_only=False):
            super(MyIntegerField, self).contribute_to_class(cls, name, private_only)
            if self.choices is not None:
                display_override = getattr(cls, 'get_%s_display' % self.name)
                setattr(cls, 'get_%s_display_override' % self.name, display_override)
    
  2. In your class, replace your choice field with MyIntegerField:

    class A(models.Model):
       unit = MyIntegerField(choices=something)
    
  3. Finally, use the copy function to return the super value:

       def get_unit_display(self, value):
           if your condition:
              return your value
           return self.get_unit_display_override()
    
Toby Speight
  • 27,591
  • 48
  • 66
  • 103
gapson
  • 1
  • 2
0

You can't directly call super() because the original method doesn't "exist" yet on the parent model.

Instead, call self._get_FIELD_display() with the field object as its input. The field object is accessible through the self._meta.get_field() method.

def get_unit_display(self):
    singular = self._get_FIELD_display(self._meta.get_field('unit'))
    return singular + 's'
-3

You should be able to override any method on a super class by creating a method with the same name on the subclass. The argument signature is not considered. For example:

class A(object):
    def method(self, arg1):
        print "Method A", arg1

class B(A):
    def method(self):
        print "Method B"

A().method(True) # "Method A True"
B().method() # "Method B"

In the case of get_unit_display(), you do not have to call super() at all, if you want to change the display value, but if you want to use super(), ensure that you're calling it with the correct signature, for example:

class A(models.Model):
    unit = models.IntegerField(choices=something)

    def get_unit_display(self, value):
        display = super(A, self).get_unit_display(value)
        if value > 1:
            display = display + "s"
        return display

Note that we are passing value to the super()'s get_unit_display().

shakefu
  • 93
  • 4
  • 2
    This would be true if it was any other regular method but get_FOO_display is created by special Django magic, like pointed in the other answer. – Danilo Cabello Dec 15 '14 at 17:26