1

With an instance of Concert I get: unbound method do_stuff() must be called with Concert instance as first argument (got ModelBase instance instead)

models.py:

class Event(models.Model):
    def do_stuff(self):
        response self.do_specific_stuff(self)


class Concert(Event):
    def do_specific_stuff(self):
        ...

class Party(Event):
    def do_specific_stuff(self):
        ...

views:

def index(request):
    x = Concert.objects.get(name='Jack White @ Oakland')
    output = x.do_stuff()
    return HttpResponse(output)

My goal is to loop trough all the events and execute the do_specific_stuff child class method based on what kind of event it is.

Dieter
  • 441
  • 1
  • 5
  • 15

3 Answers3

3

In Django, inheritance triggers multi-table inheritance, but you don't get the polymorphism in Python. It's just an instance of the ORM not providing a perfect correspondence between the data schema and the object model.

In other words, when you query Event, you get back a whole bunch of Event objects, regardless of whether some of them are actually Concert or Party objects. You have to manually downcast. If an Event is a Concert, it will have an attribute called concert, which points to the corresponding Concert subclass. Likewise for Party. If it is just a normal Event, it will have neither attribute.

You could use the following property on Event to automatically downcast your object:

@property
def as_child_class(self):
    """Casts this object to its subclass, if possible"""

    if hasattr(self, 'concert'):
        return self.concert
    elif hasattr(self, 'party'):
        return self.party
    else:
        return self

Then you could do something like:

for event in Event.objects.all()
    event.as_child_class.do_specific_stuff()

Similar questions have come up before:

And this link has some other ideas:

Community
  • 1
  • 1
acjay
  • 34,571
  • 6
  • 57
  • 100
  • That makes sense! How do I use this? Let me try that! – Dieter Nov 20 '12 at 15:35
  • Only if you're calling it on Event objects would you want to do `self.as_child_class.do_specific_stuff()`. You don't need to manually pass self to any of the functions, and because `as_child_class` is a property, you can treat it like you're retrieving a member variable. Check my edit out, for code you might call from a view. – acjay Nov 20 '12 at 15:38
0

It seems to me that your Event model is ONLY for inheritance, so you should abstract it:

class Event(models.Model):
    class Meta:
        absract = True

    def do_stuff(self):
        response self.do_specific_stuff()

    def do_specific_stuff(self):
        raise NotImplemented


class Concert(Event):
    def do_specific_stuff(self):
        ...


class Party(Event):
    def do_specific_stuff(self):
        ...

I may be wrong about your usage of Event, but if it was, abstracting your Event model will have it to be like a plain class, I mean no database actions will be taken for such model.

Hope this helps! :)

Gerard
  • 9,088
  • 8
  • 37
  • 52
0

First of all, see Template method

Secondly, the Event class should be abstract.

class Event:
   def __init__:
       raise NotImplemented('This class is abstract')

Thirdly, see Single Table Inheritance and Class Table Inheritance patterns. And django-ORM realisation of the patterns there Good luck =)

Alexey Sidash
  • 441
  • 4
  • 14