Model Inheritance Questions

17 views
Skip to first unread message

Brian

unread,
Aug 12, 2008, 11:30:56 PM8/12/08
to Django users
I have some questions around how model inheritance works and should
best be used.

Reusing the Place/Restaurant example in the website documentation,
suppose I have classes Place and subclass Restaurant:

class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)

class Restaurant(Place):
serves_hot_dogs = models.BooleanField()

Next suppose I want a directory of places (both restaurants and non-
restaurants). I could add these classes:

class Directory(models.Model):
pass

class PlaceEntry(models.Model):
place = models.ForeignKey(Place)
directory = models.ForeignKey(Directory)

If I access an entry in the directory, how do I tell if it's a
Restaurant or just a Place? If it's a Restaurant, how do I access it
as one?

Basic tests seem to fail: e.g. isinstance(PlaceEntry.objects.all()
[1].place, Restaurant) returns False.

I may be thinking about models and inheritance all wrong, so if so
could someone please offer some advice on the right way to go about
this, I'll be very appreciative.

Thanks in advance.

Vance Dubberly

unread,
Aug 13, 2008, 1:41:00 AM8/13/08
to django...@googlegroups.com
Model Inheritence implicitly creates a OneToOneField in the child
class. So you can think of your classes kind of like this:

class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)


class Restaurant(Place):
serves_hot_dogs = models.BooleanField()

parent = models.OneToOne(Place)

which means normal rules apply you should be able to do this

place = new Place.objects.get(pk=1)
restaurant = place.restaurant

if restaurant isn't defined in your place then place isn't a restaurant.

I'd imagine you prolly have something more sophisticated like:

Place()
Restaurant(Place)
Theater(Place)
CrackHouse(Place)

Which could make life interesting depending on what you're doing...
I'd imagine some tricky if else or try/except statments would help you
figure out which class your child is. Somebody smarter than I would
have to help you with that.

Vance

--
To pretend, I actually do the thing: I have therefore only pretended to pretend.
- Jacques Derrida

Andrew Ingram

unread,
Aug 13, 2008, 5:19:11 AM8/13/08
to Django users
Model Inheritance doesn't seem to be as useful as I hoped it would be.

I'm not too clued in on the pythonic way of how inheritance works, but
coming from a java background i'd expect the following behaviour:

class Place(models.Model):
name = models.CharField(max_length=50)

def do_name(self):
return name

class Restaurant(Place):

def do_name(self):
return "I am a restaurant named %s", name


If I create and save a restaurant, then do Place.objects.all(), i'd
expect to get a list containing a Restaurant object, rather than a
simple Place object with a restauraunt field. One of the major uses of
inheritance in my experience is overriding the behaviour for execution
of the same method. I don't want to have to do a try/except on all the
possible subclass fields just to be able to use it, it kind of defeats
the point.

I want to be able to iterate over the do_name method in
Place.objects.all() and get something like:

place1
place2
I am a restaurant named place3
I am a restaurant named place4
place5
I am a restaurant named place6

This week i've been trying to use inheritance for a competition app
where you can have different subclass of competitions each with
different entry forms. I was hoping I could just override a get_form
method on the subclasses but it doesn't work because the view queries
the Competition queryset rather than the subclasss.

My solution would be to add an automatically generated field called
concrete_content_type or something that is automatically added to
every model, that way it would be possible to know which type to
instantiate when you do a query on the superclass table, problem is
that this would break existing django apps.

- Andrew Ingram

Malcolm Tredinnick

unread,
Aug 13, 2008, 10:33:39 AM8/13/08
to django...@googlegroups.com

On Wed, 2008-08-13 at 02:19 -0700, Andrew Ingram wrote:
> Model Inheritance doesn't seem to be as useful as I hoped it would be.
>
> I'm not too clued in on the pythonic way of how inheritance works,

You're assuming the current behavioural restrictions are imposed by
Python. They're not. They exist because we are providing a Python
representation of rows from a relational database. There's no direct
equivalent to SQL table-like inheritance in Python because if you
subclass a model, all instances look like the appropriate child class.
That isn't true at the database level (which is more like C/C++-style
inheritance). The difference is, though, that in C++, instantiation can
add extra stuff into the in-memory representation whereas we cannot add
extra stuff to the database to change things we may not own.

[...]


> My solution would be to add an automatically generated field called
> concrete_content_type or something that is automatically added to
> every model, that way it would be possible to know which type to
> instantiate when you do a query on the superclass table, problem is
> that this would break existing django apps.

More importantly, it would make it impossible to subclass third-paty
models, which is why we don't do this. Changing somebody else's database
table just because you are subclassing it is bad form. You may not even
have the necessary database permissions to do so in all cases (e.g.
multiple database cases).

For situations where you are creating all the models in the hierarchy
and if your situation requires querying the base object to descend to
the children, then you should definitely add a type field. It's the
natural way to do things if you're wanting to implement this as
inheritance. On the other hand, you may just prefer to be explicit and
not represent that situation as inheritance at all: simply use
one-to-one fields so that your code accurately reflects what is going on
at the data storage level. Django allows both approaches, so pick
whichever methods suits you taste best.

Regards,
Malcolm


Andrew Ingram

unread,
Aug 13, 2008, 11:57:56 AM8/13/08
to Django users
On Aug 13, 3:33 pm, Malcolm Tredinnick <malc...@pointy-stick.com>
wrote:
> On Wed, 2008-08-13 at 02:19 -0700, Andrew Ingram wrote:
> You're assuming the current behavioural restrictions are imposed by
> Python. They're not.

I never made that assumption, what I did was add a disclaimer because
my experience of inheritance and OO is from the Java world and
therefore may not apply to Python. I never assumed Python was
different I just wasn't willing to assume it was the same.

> More importantly, it would make it impossible to subclass third-paty
> models, which is why we don't do this. Changing somebody else's database
> table just because you are subclassing it is bad form. You may not even
> have the necessary database permissions to do so in all cases (e.g.
> multiple database cases).

How is populating someone else's database table with a value any
different to what we already do with inheritance? My suggestion wasn't
that we alter third party models, it was that it becomes a django-
level piece of functionality, all model tables would have a field to
that represents the final class of a given row. I never said my idea
was a good one, I'm fully aware of the significant implications of a
change that would effectively break every django app in existance and
require adding a new field to every table. What I suggested was the
"obvious" solution, even if it is a hugely impractical one.

The problem I have is that what I outlined in my post is what I would
consider to be the biggest obvious use of inheritance in a project and
it seems Django's implementation of model inheritance doesn't support
it. If it's possible i'd like to see some real-life applications of
Django's model inheritance beyond use as a bit of DRY-functionality
for things like a DateMixin.

Please don't confuse this with hostility, I honestly would like to
find a use for model inheritance since I know a lot of work went into
it.

Malcolm Tredinnick

unread,
Aug 13, 2008, 12:16:40 PM8/13/08
to django...@googlegroups.com

On Wed, 2008-08-13 at 08:57 -0700, Andrew Ingram wrote:
[...]

> Please don't confuse this with hostility, I honestly would like to
> find a use for model inheritance since I know a lot of work went into
> it.

I'm not thinking you're hostile, fear not. Just trying to point you in
the right direction as far as developing an intuition about why things
behave and are implemented in this particular fashion. You're asking a
question that's been asked and answered on this list before (but this
list is of high enough volume that finding that stuff can require a bit
of searching, so I don't mind periodic repetition).

Model inheritance for the persistence layer isn't likely to have really
broad uses, primarily because object-oriented data structures don't mix
well with relational databases (search for "vietnam of computer science"
when you have a spare evening some time). All it's really intended for
is to provide a convenient way to extend third-party models. Usually the
answer to "should I use model inheritance for..." is "no". Just use
explicit one-to-one relations so that things are clear. Sometimes it's a
sufficient time-saver to warrant usage, but the really common use-case
seems to be abstract base classes as a way of factoring out common data,
not querying base classes and then narrowing the results to the
descendents (which is still possible, but you either have to use extra
queries or set your models up appropriately in the first place).

We did discuss requiring an extra column on every table to support this,
but decided it wasn't a good idea. It isn't necessarily the common case
(it's always difficult to determine what *is* a really common, but this
isn't a normal use of relational data structures, which is the guiding
factor) and it is an extra column that's needed everywhere, even on
tables that aren't subclassed. This isn't a trivial point because if
you're using this mode of operation regularly, that column should be
indexed to speed up joins, but if you're not using it, the extra index
is just a performance drag on inserts. Having everybody pay the
performance penalty no matter what their data size or usage patterns was
something we decided against.

Regards,
Malcolm


Reply all
Reply to author
Forward
0 new messages