Relational databases are not the greatest storage destination for polymorphic data structures (stack overflow). Polymorphism allows you to have heirarchies of objects that perform similar actions. By defining this action once, you can re-use it for its subclasses. It makes code cleaner and easier to maintain. I experimented with a few different ways of doing some of my subclassing and came away with an approach involving mixins that I really like so far.
Django has the ability to allow subclassing. So you can create code like:
class Vehicle(models.Model): horsepower = models.FloatField() def rev_engine(self): ... class Corvette(Vehicle): def impress_the_ladies(self): ... class MackTruck(Vehicle): def blow_horn(self): ...
and create MackTrucks and Corvettes just fine,
>> truck = MackTruck() >> truck.save() >> vette = Corvette() >> vette.save()
but if you want to pull them out as their superclass, Vehicle, and use polymorphism to call either
blow_horn(), you are out of luck. All of the objects from the Vehicles queryset come out as Vehicle objects (by default)
>> Vehicles.objects.all() [<vehicle object ><vehicle object >]
There are a few ways around this, none of which are that appealing to me.
1. One is the use of the django snippet InheritanceClassModel. You would make your base class inherit from this class and it would do some magic to allow you to call
vehicle.cast() to get either your Corvette or MackTruck object back.
What is this magic? Well, the InheritanceClassModel uses the ContentType class that comes in django.contrib to keep track of class types in the database. So when it stores your Corvette objects, it also stores an entry in a class-lookup table so that when you call
vehicle.cast(), it knows what sort of subclass to give you.
Sounds magical? Well there are drawbacks:
using InheritanceClassModel can impose serious performance hits on your queries because it has to look up not only the objects, but for each time you call
.cast(), it must make an additional query on the class look-up table. Additionally, using filtering and searching on these class trees is sketchy at best, and many times you can’t use
.filter() on the vehicle query set.
2. Another way around this is to create a custom queryset manager (stack overflow). Custom managers can be powerful, but the same fundamental problem comes about where you have to use the
ContentType object to store class types in a look-up table.
3. Another thing Django supports is mixins, and abstract base classes. The code above might be written:
class Vehicle(models.Model): horsepower = models.FloatField() def rev_engine(self): ... class Meta: abstract = True class Corvette(Vehicle): def impress_the_ladies(self): ... class MackTruck(Vehicle): def blow_horn(self): ...
Now, when Django is creating the tables relating to each class, this abstract portion of the
Vehicle class tells it that all other classes which inherit from it should have those attributes. This will make a
mack_truck table which has a
horse_power field as well as a
corvette table that has a
I’m sure you can see the potential for very wide tables, but I think those tables are much easier to read and maintain than piecing together class look-up tables.
There are plenty of Django polymorphism solutions, so what have you used for your Django subclassing? Have you had good experiences with InheritanceCastModel?