Model inheritance API

50 views
Skip to first unread message

Malcolm Tredinnick

unread,
Jul 22, 2006, 10:37:55 PM7/22/06
to django-d...@googlegroups.com
Rather than watch the "inherit from User" thread go round and round,
maybe I should give people something more concrete to think about.

This is a follow-up to the mail I sent late on Friday. It describes the
area where we need API additions or some kind of semi-major change to
incorporate model inheritance and some developer feedback would be a
good idea. I am only talking about the API here, not the implementation
(since you will notice there is no patch attached to the email).

Model inheritance (as I've implemented it) comes in two varieties,
differentiated by the way they store the data at the db level and by the
way you use them at the Python level.

-----------------------
1. Abstract Base class
-----------------------
One use-case for subclassing is to use the base class as a place to
store common fields and functionality. It is purely a "factor out the
common stuff" holder and you don't ever intend to use the base class on
its own. In the terminology of other languages (e.g. C++, Java), the
base class it is an abstract class.

At the database level, it makes sense to store the fields from the base
class in the same table as the fields from the derived class. You will
not be instantiating the base class (or querying it) on its own, so we
can view the subclassed model as a flattened version of the parent +
child. For example,

class Thing(models.Model):
name = models.CharField(...)

class Animal(Thing):
genus = models.CharField(...)

class Toy(Thing):
manufacturer = models.CharField(...)

would generate two database tables (Thing does not get its own table).
The Animal table would have "name" and "genus" columns (along with the
standard extras like "id") and Toy would have "name" and "manufacturer"
columns.

We need a way to tell Django that "Thing" is an abstract class. I would
propose

class Thing(models.Model):
name = ...

class Meta:
is_abstract = True

Points To Ponder
================
(1) What notation to use to declare a class as abstract? I've thrown out
Meta.is_abstract -- any preference for alternatives?

(2) Any strong reason not to include this case? It's only for "advanced
use", since there are a few ways to shoot yourself in the foot (e.g.
declare a class abstract, create the tables, remove the abstract
declaration, watch code explode). However, it will be useful in some
cases. [I have some scripts to help with converting non-abstract
inheritance to abstract and vice-versa at the database level, too.]

---------------------------
2. "Pythonic" Inheritance
----------------------------
The traditional Python inheritance model allows one to create instances
of the base class and work with instances of base classes as though they
were the parent. Extending this to Django's querying model, we should be
able to run queries against the Parent class:

Thing.objects.filter(name = "horse")

in the above example and, through the magic of duck typing, have the
right sort of object returned.

Amazingly enough, this is also supported in my code. It follows natural
the multi-table model of doing inheritance (in the above classes, Animal
would have a foreign key reference to the Thing table, similarly for
Toy). Multiple inheritance works as well.

In order to make duck typing work without doing tons of extra database
queries, it is necessary to have a mapping between each row and the type
of object it ultimately represents. So a row in the Thing table for an
Animal object would say "I am an Animal" (we already know it's a Thing,
because it's in the Thing table). The reverse direction (a row in the
Animal table is also a Thing) is easy because we have the model
description at the Python level and the foreign key constraint at the
database level (the latter being more of a theoretical construct if
you're using SQLite, but that's not a showstopper). When thinking about
this, realise that it works smoothly for multi-layer inheritance: it
only takes two queries to get all the data for any object, even if you
start by querying the top table in the hierarchy.

I have currently implemented this "downwards" reference as an extra
column in each table that is a foreign key to the ContentType table (to
head off one question: a GenericForeignKey field adds nothing here).
Every single table would need this column because you never know when
somebody is going to subclass that model and every query needs to
retrieve that value as well.

The alternative is to have a separate table that has columns for
content_type, pk_value, derived_content_type that performs the same
function. The drawback of this is that every single query needs to do a
join with this second table, because we need to know the derived class
in order to create the right type of object back in Python land (a query
on the Thing table for something that is ultimately an Animal should
return an Animal instance; that is how Python works and we don't want to
go and introduce C++-style casts).

Points To Ponder
================
(3) I think having the downward reference column (the one that specifies
the type of the most-derived object) as a column on each table is the
right approach. Anybody have strong arguments for the other approach (a
separate table)?

- we can ship a script that helps with conversion from existing
tables to the new structure. There is no strict requirement on
what this new column is called -- it can be configured on a
per-model basis so that we don't restrict anybody's particular
column choices.

-----------------------
3. What you don't get
-----------------------
I am avoiding PostgreSQL's table inheritance feature. It is not a
standard feature on databases, so we have to do inheritance inside
Django anyway and having to maintain both the Django version and the
PostgreSQL-specific version will lead to errors (we already have enough
per-database specific stuff to discourage anybody from wanting more at a
whim).

I am not implementing the "everything in one table" storage model. It is
not universally applicable (you can't inherit from third-party models,
for a start) and I have software engineering objections to it. It also
doesn't add much value at this point in time. If somebody wants that
later, that is for later.

I am not doing anything view-based. Most database compute views on
demand, so they don't add any real performance value and hide some
complexity (the complexity is already hidden from the Python user
anyway. But hiding it from the developer trying to fix core bugs is not
a good plan).

All of the above features can be worked on by others if they like (free
and open source and all that).

If we can get the query SQL generation stuff sorted out next week when a
few of us are in one place, fixing the model inheritance patch to work
with that is all that remains to do before I can submit it. Currently I
do get bitten by some query generation bugs, so it's not an either/or
situation.

Malcolm

Ivan Sagalaev

unread,
Jul 23, 2006, 5:02:01 AM7/23/06
to django-d...@googlegroups.com
First of all: Malcolm, this looks damn cool and very well thought out!
Thank you!

Malcolm Tredinnick wrote:
> ---------------------------
> 2. "Pythonic" Inheritance
> ----------------------------

> ...


>
> (in the above classes, Animal
> would have a foreign key reference to the Thing table, similarly for
> Toy).

Why foreign key? This means that data of one Thing object can be shared
between two different Animal objects. That seems strange, I'd rather
expected it to be one-to-one relation.

Malcolm Tredinnick

unread,
Jul 23, 2006, 6:39:46 AM7/23/06
to django-d...@googlegroups.com
On Sun, 2006-07-23 at 13:02 +0400, Ivan Sagalaev wrote:
> First of all: Malcolm, this looks damn cool and very well thought out!
> Thank you!

Thanks.

>
> Malcolm Tredinnick wrote:
> > ---------------------------
> > 2. "Pythonic" Inheritance
> > ----------------------------
> > ...
> >
> > (in the above classes, Animal
> > would have a foreign key reference to the Thing table, similarly for
> > Toy).
>
> Why foreign key? This means that data of one Thing object can be shared
> between two different Animal objects. That seems strange, I'd rather
> expected it to be one-to-one relation.

Good catch. :-)

It's a typo; the code is using a variation on OneToOneField there. I
have to hack around things a bit for multiple inheritance, since pure
Django OneToOneFields want to be primary keys and you can only have one
of those. But it is a one-to-one relation to each parent.

Regards,
Malcolm


Bill de hÓra

unread,
Jul 23, 2006, 12:12:40 PM7/23/06
to django-d...@googlegroups.com
Malcolm Tredinnick wrote:

> -----------------------
> 1. Abstract Base class
> -----------------------

> [...]


>For example,
>
> class Thing(models.Model):
> name = models.CharField(...)
>
> class Animal(Thing):
> genus = models.CharField(...)
>
> class Toy(Thing):
> manufacturer = models.CharField(...)
>
> would generate two database tables (Thing does not get its own table).
> The Animal table would have "name" and "genus" columns (along with the
> standard extras like "id") and Toy would have "name" and "manufacturer"
> columns.

I bet people will want to compare and load these things (no pun
intended) via the DBAPI ("gimme all the Things, I'll sort them out").
Point being, if you give people superclasses, they'll want to use them.
Will there be a way to polymorphically associate Animal and Toy with
Thing behind the scenes so that I work at the highest level of
abstraction, via the Thing class?


> Points To Ponder
> ================
> (1) What notation to use to declare a class as abstract? I've thrown out
> Meta.is_abstract -- any preference for alternatives?

A marker interface: models.AbstractModel. That gives you a place to put
support functionality if you need it later (like polymorphic
association). It indicates the class is fundamentally different (which
I think it is). I suspect people are less likely to mess around with
inheritance chains than field settings on the Meta class.


> Points To Ponder
> ================
> (3) I think having the downward reference column (the one that specifies
> the type of the most-derived object) as a column on each table is the
> right approach. Anybody have strong arguments for the other approach (a
> separate table)?

I'm aware that hibernate doesn't use a type discriminator in the
supertable, on the basis it's more true to relational thinking. I have
no idea how they made it performant.


> -----------------------
> 3. What you don't get
> -----------------------

> [...]


> I am not implementing the "everything in one table" storage model. It is
> not universally applicable (you can't inherit from third-party models,
> for a start) and I have software engineering objections to it.

Why? NOT NULL constraints in the children?

cheers
Bill

Jacob Kaplan-Moss

unread,
Jul 23, 2006, 12:31:56 PM7/23/06
to django-d...@googlegroups.com
On Jul 22, 2006, at 9:37 PM, Malcolm Tredinnick wrote:
> Rather than watch the "inherit from User" thread go round and round,
> maybe I should give people something more concrete to think about.

W00t -- I'm about +1,000 on your proposal; it's well-thought-out,
clean, and seems to hit all the right notes.

I've got a few questions, obviously, but nothing major. You've
certainly got *my* blessing to go forward with this.

> (1) What notation to use to declare a class as abstract? I've
> thrown out
> Meta.is_abstract -- any preference for alternatives?

Bill de hÓra suggests ``models.AbstractModel``... hm...

I think I agree with him that ``AbstractModel`` looks cleaner -- I
like seeing that a model is abstract right there at the top rather
than having to read down to ``Meta`` to see what's going on.

However, what about the case when I want to do something with more
than one level of abstract models. Say I want abstract ``Shape``,
``2DShape``, and ``3DShape`` classes (where 2D and 3D extend
``Shape``)... How would that work with your syntax, Bill?


> (2) Any strong reason not to include this case? It's only for
> "advanced
> use", since there are a few ways to shoot yourself in the foot (e.g.
> declare a class abstract, create the tables, remove the abstract
> declaration, watch code explode). However, it will be useful in some
> cases. [I have some scripts to help with converting non-abstract
> inheritance to abstract and vice-versa at the database level, too.]

I think abstract base classes are *key*, actually -- nearly every use
I'm thinking of includes 'em.

> The traditional Python inheritance model allows one to create
> instances
> of the base class and work with instances of base classes as though
> they
> were the parent. Extending this to Django's querying model, we
> should be
> able to run queries against the Parent class:
>
> Thing.objects.filter(name = "horse")
>
> in the above example and, through the magic of duck typing, have the
> right sort of object returned.

So I'd get back an iterator that yields Things, Animals, and Toys?
That's what I'd expect, and want.

> (3) I think having the downward reference column (the one that
> specifies
> the type of the most-derived object) as a column on each table is the
> right approach. Anybody have strong arguments for the other
> approach (a
> separate table)?

I think I'd need to see the code to understand what this extra column
does, but a "magic" inheritance table seems a little scary to me and
I'd be inclined against it.

As long as I get to choose what that extra column is called... oh,
reading below shows that you've already addressed that; cool.

Awesome. I'm looking forward to seeing the code!

Jacob

Malcolm Tredinnick

unread,
Jul 23, 2006, 7:19:49 PM7/23/06
to django-d...@googlegroups.com
On Sun, 2006-07-23 at 17:12 +0100, Bill de hÓra wrote:
> Malcolm Tredinnick wrote:
>
> > -----------------------
> > 1. Abstract Base class
> > -----------------------
> > [...]
> >For example,
> >
> > class Thing(models.Model):
> > name = models.CharField(...)
> >
> > class Animal(Thing):
> > genus = models.CharField(...)
> >
> > class Toy(Thing):
> > manufacturer = models.CharField(...)
> >
> > would generate two database tables (Thing does not get its own table).
> > The Animal table would have "name" and "genus" columns (along with the
> > standard extras like "id") and Toy would have "name" and "manufacturer"
> > columns.
>
> I bet people will want to compare and load these things (no pun
> intended) via the DBAPI ("gimme all the Things, I'll sort them out").
> Point being, if you give people superclasses, they'll want to use them.
> Will there be a way to polymorphically associate Animal and Toy with
> Thing behind the scenes so that I work at the highest level of
> abstraction, via the Thing class?

I wasn't planning on it. Well, I was, in that you do it by not marking
Thing as abstract! "Will never instantiate one of these" is a guiding
principle for this abstract concept. Yes, it's non-Pythonic; it's an
accomodation to the database model for optimisation purposes only. Most
of the time, people should just trust that their database was built by
professionals and will be fast enough and just use normal inheritance.
If you want to use abstract classes, then you are committing to further
restrictions.

That being said, it's not something I'm against per se. I just can't see
how it will work easily. We can look at this in due course, though. It's
added functionality, so something we could even put in later if we
wanted to (that is to say, interesting idea, but not my focus right now
unless you can convince me it's a showstopper for some reason beyond
"people can't read the instructions").

[...]


> > Points To Ponder
> > ================
> > (3) I think having the downward reference column (the one that specifies
> > the type of the most-derived object) as a column on each table is the
> > right approach. Anybody have strong arguments for the other approach (a
> > separate table)?
>
> I'm aware that hibernate doesn't use a type discriminator in the
> supertable, on the basis it's more true to relational thinking. I have
> no idea how they made it performant.

Interesting. Maybe it's just universally sucky and one doesn't notice
the extra overhead. :-) Hibernate has a reasonable rep, though. Hmm...

I might have a quick look at what they're doing. Without a
discriminator, it would seem to be impossible to avoid doing O(# of
subclasses, including non-leaf classes) queries each time, which seems
"not cool". Still, thanks for the tip.


> > -----------------------
> > 3. What you don't get
> > -----------------------
> > [...]
> > I am not implementing the "everything in one table" storage model. It is
> > not universally applicable (you can't inherit from third-party models,
> > for a start) and I have software engineering objections to it.
>
> Why? NOT NULL constraints in the children?

That's one good reason. It's also not very normalised; table design with
lots of sparse columns won't get me invited to the cool parties. Wide
rows where you only need to access a few fields tend to not always
perform as well as people might like (I'm talking about very large
datasets here; small datasets don't matter so much).

Finally, because I would like to support extending third-party models
out of the box, we need the multi-table model and I don't want/need the
extra complexity of more and more alternatives right now. That's
something somebody could add later if their continued existence depended
on it. It's all "under the covers" work.

I'm on a "pick one thing, do it well" drive at the moment (with a slight
concession to the abstract base class case).

Regards,
Malcolm


Malcolm Tredinnick

unread,
Jul 23, 2006, 7:35:49 PM7/23/06
to django-d...@googlegroups.com
On Sun, 2006-07-23 at 11:31 -0500, Jacob Kaplan-Moss wrote:
> On Jul 22, 2006, at 9:37 PM, Malcolm Tredinnick wrote:
[...]

>
> > (1) What notation to use to declare a class as abstract? I've
> > thrown out
> > Meta.is_abstract -- any preference for alternatives?
>
> Bill de hÓra suggests ``models.AbstractModel``... hm...
>
> I think I agree with him that ``AbstractModel`` looks cleaner -- I
> like seeing that a model is abstract right there at the top rather
> than having to read down to ``Meta`` to see what's going on.
>
> However, what about the case when I want to do something with more
> than one level of abstract models. Say I want abstract ``Shape``,
> ``2DShape``, and ``3DShape`` classes (where 2D and 3D extend
> ``Shape``)... How would that work with your syntax, Bill?

That's a valid problem.

[...]


> > The traditional Python inheritance model allows one to create
> > instances
> > of the base class and work with instances of base classes as though
> > they
> > were the parent. Extending this to Django's querying model, we
> > should be
> > able to run queries against the Parent class:
> >
> > Thing.objects.filter(name = "horse")
> >
> > in the above example and, through the magic of duck typing, have the
> > right sort of object returned.
>
> So I'd get back an iterator that yields Things, Animals, and Toys?
> That's what I'd expect, and want.

Yep, that's how it would work. At the moment, it's lazy, in that you get
back an object of the right type, but it's lazy and doesn't populate the
sub-class fields until you demonstrate you want them. There's a
reasonable chance you're going to be discriminating further on type
anyway, or just wanting to access the Thing fields.

I would like to have an equivalent of select_related() that allowed you
to populate all the sub-data for a collection of objects, too, I think.
That's not implemented yet, since it's not critical to the functionality
(and there are a couple of API issues because of how I can think I would
use it). So that's off to one side at the moment.

> > (3) I think having the downward reference column (the one that
> > specifies
> > the type of the most-derived object) as a column on each table is the
> > right approach. Anybody have strong arguments for the other
> > approach (a
> > separate table)?
>
> I think I'd need to see the code to understand what this extra column
> does, but a "magic" inheritance table seems a little scary to me and
> I'd be inclined against it.

Obviously seeing the code should clarify, but in the interim...

If I select a row from the Thing table, the object returned should be
the proper type (which isn't necessarily a Thing -- it could be Animal
or Toy). So I need to know what the most derived type is for this row in
order to generate the right object. Doing a scan over all possible
sub-classes to find out where the id-linking trail runs out is very
expensive. Caching content-types in memory as we access them (something
that comes up as useful in a number of situations) and doing a deep dive
to the right class immediately using this column's value is the idea.

By the way, this introduces something I didn't mention previously (it's
an implementation point): in order for model inheritance to work in this
sort of set up, it needs content types. Content types are a "contrib"
option at the moment. I'm not sure if that worries me a lot or not. It
keeps rattling around in my head, though.

Regards,
Malcolm

Russell Keith-Magee

unread,
Jul 23, 2006, 9:04:37 PM7/23/06
to django-d...@googlegroups.com

First off: +1 and then some from me - this is some great stuff, Malcolm.

On 7/24/06, Malcolm Tredinnick < mal...@pointy-stick.com> wrote:

On Sun, 2006-07-23 at 11:31 -0500, Jacob Kaplan-Moss wrote:
> On Jul 22, 2006, at 9:37 PM, Malcolm Tredinnick wrote:
[...]
>
> > (1) What notation to use to declare a class as abstract? I've
> > thrown out
> > Meta.is_abstract -- any preference for alternatives?
>
> Bill de hÓra suggests ``models.AbstractModel``... hm...
>
> I think I agree with him that ``AbstractModel`` looks cleaner -- I
> like seeing that a model is abstract right there at the top rather
> than having to read down to ``Meta`` to see what's going on.
>
> However, what about the case when I want to do something with more
> than one level of abstract models.  Say I want abstract ``Shape``,
> ``2DShape``, and ``3DShape`` classes (where 2D and 3D extend
> ``Shape``)... How would that work with your syntax, Bill?

I'm inclined to agree with Bill and Jacob on the is_abstract tag - a flag in the Meta class strikes me as something that could be very easily lost or forgotten.

I had two immediate gut reactions for an alternate syntax -
1) Multiple inheritance:

class Thing(models.Model, models.Abstract):
   ...

2) A decorator:

@models.Abstract
class Thing(models.Model ):
   ...

I haven't worked through all the details, but I'm reasonably certain either of these could be used to get around the 'multiple abstract classes' problem. Of the two, I think I prefer the decorator; it seems a little more pythonic, and the multiple inheritance version would require a bit of metaclass magic that I'm not entirely sure I'm comfortable with. However, I think I could happily live with either.

> I think I'd need to see the code to understand what this extra column
> does, but a "magic" inheritance table seems a little scary to me and
> I'd be inclined against it.

Doesn't scare me. Seems like the natural way to solve the problem. +1

By the way, this introduces something I didn't mention previously (it's
an implementation point): in order for model inheritance to work in this
sort of set up, it needs content types. Content types are a "contrib"
option at the moment. I'm not sure if that worries me a lot or not. It
keeps rattling around in my head, though.

I'm not sure this bothers me. Content types are a fairly 'meta' concept. It seems natural to have it part of the core.
 
Russ Magee %-)

Bryan

unread,
Jul 23, 2006, 11:52:06 PM7/23/06
to Django developers
+1

This is great, Malcom, and a step forward to my comments/goals/rants in
the other thread.

Russell Keith-Magee wrote:
> 2) A decorator:
>
> @models.Abstract
> class Thing(models.Model):
> ...
>

When did python support class decorators?

Jacob Kaplan-Moss wrote:
> ... I like seeing that a model is abstract right there at the top rather


> than having to read down to ``Meta`` to see what's going on.

+1

Russell Keith-Magee

unread,
Jul 24, 2006, 12:19:53 AM7/24/06
to django-d...@googlegroups.com
On 7/24/06, Bryan <ger...@gmail.com> wrote:

Russell Keith-Magee wrote:
> 2) A decorator:

When did python support class decorators?

You know... one of these days, I'm going to learn to have coffee before I touch a keyboard :-)

You are correct - the @ syntax doesn't work for classes. However, that doesn't mean you can't use a decorator with Python 2.3-style notation:

class Thing(models.Model):
   ...
Thing = models.Abstract(Thing)

(sheepishly),
Russ Magee %-)

Nebojsa Djordjevic

unread,
Jul 24, 2006, 7:32:31 AM7/24/06
to django-d...@googlegroups.com
Malcolm Tredinnick wrote:
> Rather than watch the "inherit from User" thread go round and round,
> maybe I should give people something more concrete to think about.
>
> This is a follow-up to the mail I sent late on Friday. It describes the
> area where we need API additions or some kind of semi-major change to
> incorporate model inheritance and some developer feedback would be a
> good idea. I am only talking about the API here, not the implementation
> (since you will notice there is no patch attached to the email).
>
> Model inheritance (as I've implemented it) comes in two varieties,
> differentiated by the way they store the data at the db level and by the
> way you use them at the Python level.
>
> -----------------------
> 1. Abstract Base class
> -----------------------

+1e6

Great!!! Just when I need it - I'm going through pain of maintaining four separate models with lot's of common fields.
Go for it.

> ---------------------------
> 2. "Pythonic" Inheritance
> ----------------------------

+1

even better (I'm not in any way database and/or django ORM expert so I will not comment actual implementation).

--
Nebojša Đorđević - nesh
Studio Quattro - Niš - SCG
http://studioquattro.biz/
http://djnesh.blogspot.com/ | http://djnesh-django.blogspot.com/ | http://trac.studioquattro.biz/djangoutils/
Registered Linux User 282159 [http://counter.li.org]

DavidA

unread,
Jul 24, 2006, 7:51:02 AM7/24/06
to Django developers

Malcolm Tredinnick wrote:
> On Sun, 2006-07-23 at 17:12 +0100, Bill de hÓra wrote:
> > Malcolm Tredinnick wrote:
> > > -----------------------
> > > 3. What you don't get
> > > -----------------------
> > > [...]
> > > I am not implementing the "everything in one table" storage model. It is
> > > not universally applicable (you can't inherit from third-party models,
> > > for a start) and I have software engineering objections to it.
> >
> > Why? NOT NULL constraints in the children?
>
> That's one good reason. It's also not very normalised; table design with
> lots of sparse columns won't get me invited to the cool parties. Wide
> rows where you only need to access a few fields tend to not always
> perform as well as people might like (I'm talking about very large
> datasets here; small datasets don't matter so much).
>
> Finally, because I would like to support extending third-party models
> out of the box, we need the multi-table model and I don't want/need the
> extra complexity of more and more alternatives right now. That's
> something somebody could add later if their continued existence depended
> on it. It's all "under the covers" work.
>
> I'm on a "pick one thing, do it well" drive at the moment (with a slight
> concession to the abstract base class case).

The big downside to one table to hold all data would be that if I ever
added a new derived class, I need to rebuild the table for _all_ other
classes. Unless you pair this with schema evolution, that would seem to
be a showstopper.

-Dave

Bill de hÓra

unread,
Jul 24, 2006, 8:12:31 AM7/24/06
to django-d...@googlegroups.com
Malcolm Tredinnick wrote:

> table design with
> lots of sparse columns won't get me invited to the cool parties.

Qotd!

[I should say what you're doing sounds great; just trying to figure out
where the constraints and gotchas are at.]

cheers
Bill

Gábor Farkas

unread,
Jul 24, 2006, 9:19:21 AM7/24/06
to django-d...@googlegroups.com
Bill de hÓra wrote:
> Malcolm Tredinnick wrote:
>
>> table design with
>> lots of sparse columns won't get me invited to the cool parties.
>
> Qotd!
>

(added to my quotes-collection :-)

gabor

David Blewett

unread,
Jul 24, 2006, 1:45:31 PM7/24/06
to Django developers
Jacob Kaplan-Moss wrote:
> On Jul 22, 2006, at 9:37 PM, Malcolm Tredinnick wrote:
> > Rather than watch the "inherit from User" thread go round and round,
> > maybe I should give people something more concrete to think about.

First of all, +1 on this proposal. I've been approaching this problem
from different ways for the past couple of weeks, and had settled on a
very similar one to implement myself. I was suffering from a lack of
examples of how to use the content type app though.

> > (2) Any strong reason not to include this case? It's only for
> > "advanced
> > use", since there are a few ways to shoot yourself in the foot (e.g.
> > declare a class abstract, create the tables, remove the abstract
> > declaration, watch code explode). However, it will be useful in some
> > cases. [I have some scripts to help with converting non-abstract
> > inheritance to abstract and vice-versa at the database level, too.]
>
> I think abstract base classes are *key*, actually -- nearly every use
> I'm thinking of includes 'em.

. . .


> So I'd get back an iterator that yields Things, Animals, and Toys?
> That's what I'd expect, and want.

I also think keeping the abstract class as a way of aggregating all the
subclasses is key. An iterator is exactly what I would like to see as
well.

Please post any examples / code as they come!

David Blewett

Ahmad Alhashemi

unread,
Jul 26, 2006, 10:39:14 AM7/26/06
to Django developers
Malcolm Tredinnick wrote:
> -----------------------
> 1. Abstract Base class
> -----------------------
> One use-case for subclassing is to use the base class as a place to
> store common fields and functionality. It is purely a "factor out the
> common stuff" holder and you don't ever intend to use the base class on
> its own. In the terminology of other languages (e.g. C++, Java), the
> base class it is an abstract class.
>
> At the database level, it makes sense to store the fields from the base
> class in the same table as the fields from the derived class. You will
> not be instantiating the base class (or querying it) on its own, so we
> can view the subclassed model as a flattened version of the parent +
> child. For example,
>
> class Thing(models.Model):
> name = models.CharField(...)
>
> class Animal(Thing):
> genus = models.CharField(...)
>
> class Toy(Thing):
> manufacturer = models.CharField(...)

In this case, they will simply be a list of fields with no
corresponding tables, managers, instances, data, .. etc. So they aren't
really models. Maybe we should consider them a different kind of
classes. Call them something like schema classes, or model templates.

This means that it will not be sufficient for their derivatives to
subclass them to become models, so they will be forced to use multiple
inheritance. Like this:

class Thing(models.Schema):
name = models.CharField(...)

class Animal(models.Model, Thing):
genus = models.CharField(...)

class Toy(models.Model, Thing):
manufacturer = models.CharField(...)

This alternative solution is clearer and better because:
1. It makes it clearer the fact that you cannot use Thing directly as a
model (because it is never thought of as a model anyway).

2. It makes the syntax for subclassing the abstract way and subclassing
the Pythonic way different in the definition of the derivative classes.

Two possible drawbacks:
1. It won't work if we need to inherit anything other than a commonly
used set of fields, but that should probably be the job of the Pythonic
inheritance.

2. I'm not sure weather multiple inheritance will be a feasible
solution in Python (because the code that is going to introspect the
Thing super class and add its fields to our class is model.Model, which
is one of the suer classes).

But I don't see this as a problem either, because we can always find an
alternative method of adding those fields to the model. We don't have
to use inheritance, because there is really no inheritance involved
here. We can simply declare the fact that we want to add a pre-defined
set of fields in the META section of the derivative class.

This would be the most extreme form of my reasoning:

fields_common_to_things = {name = models.CharField(...)}

class Animal(models.Model, Thing):
genus = models.CharField(...)
class META:
merge_fields = [fields_common_to_things]

class Toy(models.Model, Thing):
manufacturer = models.CharField(...)
class META:
merge_fields = [fields_common_to_things]

Ahmad Alhashemi

unread,
Jul 26, 2006, 11:47:21 AM7/26/06
to Django developers
A few corrections...

> 2. I'm not sure weather multiple inheritance will be a feasible
> solution in Python (because the code that is going to introspect the
> Thing super class and add its fields to our class is model.Model, which
> is one of the suer classes).

It is actually feasible, I just checked.


> fields_common_to_things = {name = models.CharField(...)}

Small typo here. This should have been:

fields_common_to_things = {'name': models.CharField(...)}

Reply all
Reply to author
Forward
0 new messages