views:

311

answers:

5

I have a model

BaseModel

and several subclasses of it

ChildModelA(BaseModel), ChildModelB(BaseModel), ...

using multi-table inheritance. In future I plan to have dozens of subclass models.

All subclasses have some implementation of method

do_something()

How can I call do_somthing from a BaseModel instance?

Almost identical problem (without solution) is posted here:
http://peterbraden.co.uk/article/django-inheritance

A simpler question: how I resolve BaseModel instnace to one of its subclasses instance without checking all possible subclasses?

+1  A: 

Will you ever be working with an instance of the base type or will you always be working with instances of the children? If the latter is the case then call the method, even if you have a reference to the base type since the object itself IS-A child type.

Since Python support duck typing this means that your method call will be bond appropriately since the child instance will truly have this method.

A pythonic programming style which determines an object’s type by inspection of its method or attribute signature rather than by explicit relationship to some type object (“If it looks like a duck and quacks like a duck, it must be a duck.”) By emphasizing interfaces rather than specific types, well-designed code improves its flexibility by allowing polymorphic substitution. Duck-typing avoids tests using type() or isinstance(). (Note, however, that duck-typing can be complemented with abstract base classes.) Instead, it typically employs hasattr() tests or EAFP programming.

Note that EAFP stands for Easier to Ask Forgiveness than Permission:

Easier to ask for forgiveness than permission. This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements. The technique contrasts with the LBYL style common to many other languages such as C.

Andrew Hare
This is all correct, but not relevant in this case. The issue is precisely that when working with Django multi-table inheritance, one often has an instance of the base class available (i.e. from querying the base table), even though the object is "really" of a child type (i.e. has a corresponding entry in a child table).
Carl Meyer
A: 

I agree with Andrew. On a couple of sites we have a class that supports a whole bunch of methods (but not fields (this was pre-ORM refactor)) that are common to most-but-not-all of our content classes. They make use of hasattr to sidestep situations where the method doesn't make sense.

This means most of our classes are defined as:

class Foo(models.Model, OurKitchenSinkClass):

Basically it's sort of a MixIn type of thing. Works great, easy to maintain.

Peter Rowell
+1  A: 

If you want to avoid checking all possible subclasses, the only way I can think of would be to store the class name associated with the subclass in a field defined on the base class. Your base class might have a method like this:

def resolve(self):
    module, cls_name = self.class_name.rsplit(".",1)
    module = import_module(module)
    cls = getattr(module, cls_name)
    return cls.objects.get(pk=self.pk)

This answer does not make me happy and I too would love to see a better solution, as I will be facing a similar problem soon.

pcardune
This is essentially what you have to do, but there are cleaner ways to do it (using contrib.contenttypes). See my answer.
Carl Meyer
A: 

May be abstract Models will help you?

Oduvan
+1  A: 

See my answer to the same question here.

Carl Meyer
thank you Carlyour solution works for me :)I agree, it would be nice to have "native" solution in future versions of Django :)
Yury Lifshits