Malcolm Tredinnick posted a note this morning on the Django users group asking for people to test the nearly complete queryset-refactor branch of Django. Malcolm indicates that it's very close to merging into the main (trunk) Django distribution.

This is great news. I've made limited use of this branch because the large production project I'm working on requires more stability than it could guarantee. But after looking it over, I'm now tempted to give it a go because the new features are very exciting.

Model Inheritance

Of the many changes included in this branch, model inheritance may have the biggest impact. In previous versions of Django it wasn't possible to create model classes that inherited from anything besides models.Model. Anything you wanted to store in the database back-end had to derive directly from Model and there was no flexibility in writing your own base classes.

When this branch goes live it will eliminate this restriction. You will be able to create new base classes that inherit models.Model and then write classes that derive from these custom bases. This is just like the regular Python class system.

The queryset-refactor documentation mentions two kinds of model inheritance:

  1. Abstract base classes
  2. Multi-table inheritance

Abstract Base Classes

Abstract base classes are designed for situations where the data elements of the base class will be common to multiple child classes but never used individually. If you specify a class as abstract, database tables will be created and information stored for child classes only. The base class will not have database tables or managers or any of the usual model plumbing. It's simply a container.

To define a base class as abstract, create an inner Meta class and set it's abstract attribute to True. Consider this example from the new documentation:

    class CommonInfo(models.Model):
        name = models.CharField(max_length=100)
        age = models.PositiveIntegerField()

        class Meta:
            abstract = True

    class Student(CommonInfo):
        home_group = models.CharField(max_length=5)

Children of abstract base classes inherit their parent's inner Meta class, so any additional meta attributes specified in the base will be included in children. However, the Meta classes abstract attribute will ALWAYS be set to False in child classes.

Children of abstract base classes that define their own Meta will override their parent's unless the child's inner Meta class inherits from the parent. This allows for fine-grained control of Meta information from within child classes. For example

 class Student(CommonInfo):

       class Meta(CommonInfo.Meta):
             # additional attributes defined here

Multi-Table Inheritance

The second type of model inheritance is the multi-table technique. In this case a child class inherits from any regular model class as a base. The base class is not abstract and has it's own database tables, managers, etc. Any child classes will include the attributes of their parents, but these attributes will be stored in different tables.

Multi-table inheritance uses an automatic OneToOneField to create this relationship between parent and child. The by product of this approach is that one can query parent or child objects separately, ignorant of any downstream inheritance relationships.

Consider the example from the queryset-refactor documentation:

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

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

These simple model definitions present several interesting query scenarios. Suppose you'd like to see all places in the database located in Boston, Massachusetts:

Place.objects.filter("address__icontains=Boston MA")

Or, perhaps you'd like to limit the filter to only restaurants in Boston:

Restaurant.objects.filter("address__icontains=Boston MA")

This level of flexibility promises to be very powerful. It allows for many different pieces of content to share similar characteristics, but mold themselves to fill a particular niche. Consider a newspaper that has Article objects and can now further differentiate these objects as FrontPageArticle, SportsArticle, or OBitArticle.

Since the relationship is handled via an automatic OneToOneField, it is possible to access the child attributes of base classes directly. Using the above example, consider a Place object, p, that happens to be a restaurant:

>>> p.restaurant.serves_pizza
True

Inner Meta classes are not passed on to child classes under multi-table inheritance, except for a few special attributes (ordering, for example).

Another caveat about multi-table inheritance occurs with ForeignKey or ManyToMany fields in children. Since a OneToOneField is already in use (see above), you must specify related_name when relating attributes in children to parent classes. See the documentation for more explanation.

More Information

Additional information is available in the model documentation from the queryset-refactor branch. The branch team looks to have done a great job with this and I'm excited to begin using it.

Comments

No one has posted any comments yet. How about it, tiger?

OpenID Login

To leave a comment, sign-in with your OpenID.