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?

Reply via email to