Semantics of subtyping

107 views
Skip to first unread message

Robert Wittams

unread,
Sep 26, 2005, 12:21:37 PM9/26/05
to django-d...@googlegroups.com
I have been thinking about the way subtyping (inheritance) works in django.

Currently (correct me if I am wrong), a model class that inherits from
another model class is treated as a copy of its supertype.

This means it makes a copy of all the fields and methods, possibly
suppressing, and can also add its own fields and methods.

This means a few things ( again, correct me if I am wrong.)
* Class hierarchies do not share identity, and each type is a disjoint
set. It is not possible to lookup an animal with id 100 and get back a
dog or a cat. The corollary of this is that it is not possible to have a
foreign key to a supertype, and be able to link to any subtype.

* The generated classes are not "really" subclasses of their parent, and
the reason they can't be is that they can suppress members.

The other form of subtyping - ( ie is-a relationship) available, is the
one-to-one field. This makes a set of fields an extension to another
object. The way this is generated in django is to add
get_<extension_type_name> to each instance of the supertype. A base type
can have any combination of extensions.


I would suggest that the second type of subtyping is a more natural fit
for python inheritance of model classes. In a new version, current
subtypes would be changed from:

class MyArticle(Article):
...fields...
class META:
module_name = 'my_articles'
remove_fields = ...some fields...

to

class MyArticle(meta.Model):
...fields...
class META:
copy_from = Article
remove_fields = ...some fields...



OneToOneFields would change from

class Restaurant(meta.Model):
place = meta.OneToOneField(Place)
serves_hot_dogs = meta.BooleanField()
serves_pizza = meta.BooleanField()

def __repr__(self):
return "%s the restaurant" % self.get_place().name

to

class Restaurant(Place):
serves_hot_dogs = meta.BooleanField()
serves_pizza = meta.BooleanField()

def __repr__(self):
return "%s the restaurant" % self.get_place().name


the generated class would be a subclass of place, and lookups for place
would always left join to Restaurant. Any time there were results in a
subtype field, that subtype would be instantiated. So no get_<extension>
functions would be necessary. Also, multiple extensions at the same time
would not be permitted unless explictly allowed eg

class Place(meta.Model):pass
class Restaurant(Place):pass
class College(Place):pass
class CateringCollege(Restaurant, Place):pass

In this case, a combination would be allowed due to the diamond
inheritance. Inheriting from two 'top level' (ie primary key defining)
models would be an error.

There are clearly back-compat problems here : I think it would be
possible to handle them with either an extra field in META, or making
classes which want to do this have their superclass be
meta.SupertypeModel. Then either of these could be removed after a
switch over period. I think the first is probably better, as it is
easier to ignore in future, and also would be explicit on each subtype.

The main reason I think this would be a good idea is that it would make
model subtypes a lot more similar to normal python subtypes, ie they
would share identity and really be subclasses, so eg virtual functions
would work as expected.

What do people think of this?

Jason Davies

unread,
Sep 26, 2005, 5:26:21 PM9/26/05
to Django developers
Robert Wittams wrote:

[snip]

> The other form of subtyping - ( ie is-a relationship) available, is the
> one-to-one field. This makes a set of fields an extension to another
> object. The way this is generated in django is to add
> get_<extension_type_name> to each instance of the supertype. A base type
> can have any combination of extensions.
>
>
> I would suggest that the second type of subtyping is a more natural fit
> for python inheritance of model classes.

[snip]

That sounds like a good solution. Preserving the semantics of Python's
subclassing is important so that things are intuitive and work as
expected.

Would this have any bearing on solving ticket #529 (GenericForeignKey)?

An aside: will we still break backwards compatibility after milestone
1.0?

Jason

Robert Wittams

unread,
Sep 26, 2005, 5:54:15 PM9/26/05
to django-d...@googlegroups.com
Well GenericForeignKey is (imo) not necessary with 'real' subclassing.
It means that you would just do (for comments on a contrived horse
trading site):

class Comment(meta.Model):
...... subject, text, date, etc ...

class CommentReply(Comment):
parent = meta.ForeignKey(Comment)

class HorseComment(Comment):
parent = meta.ForeignKey(Horse)

class TrainerComment(meta.Modl):
parent = meta.ForeignKey(Trainer)


This of course means more tables, but I can't see that that is a problem
with any modern database.

Robert Wittams

unread,
Sep 26, 2005, 5:54:15 PM9/26/05
to public-django-developer...@ciao.gmane.org

Paul Bowsher

unread,
Sep 27, 2005, 5:43:16 AM9/27/05
to django-d...@googlegroups.com
I need some sort of GenericForeignKey thing by the end of the week,
and will be getting paid to work on it. I'll go either the subclassing
route or the GenericForeignKey route, whatever is easiest. Let me know
if you need a hand
--
Paul Bowsher
IT Systems Developer
Simply Stuck Ltd
Reply all
Reply to author
Forward
0 new messages