At the time of writing, Django latest version was 1.2
But it needs some additional elements to work.
You need to assign a custom models.Manager object for each animal model which will call its own custom QuerySet object.
Basically, instead of returning Animal
instances (this is what you get), SubclassingQuerySet
calls as_leaf_class()
method to check if item's model is Animal
or not - if it is, then just return it, otherwise perform search in its model context. Thats it.
#models.py
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.db.models.query import QuerySet
class SubclassingQuerySet(QuerySet):
def __getitem__(self, k):
result = super(SubclassingQuerySet, self).__getitem__(k)
if isinstance(result, models.Model):
return result.as_leaf_class()
return result
def __iter__(self):
for item in super(SubclassingQuerySet, self).__iter__():
yield item.as_leaf_class()
class AnimalManager(models.Manager):
def get_query_set(self): # Use get_queryset for Django >= 1.6
return SubclassingQuerySet(self.model)
class Animal(models.Model):
name = models.CharField(max_length=100)
content_type = models.ForeignKey(ContentType, editable=False, null=True)
objects = AnimalManager()
def __unicode__(self):
return "Animal: %s" % (self.name)
def save(self, *args, **kwargs):
if not self.content_type:
self.content_type = ContentType.objects.get_for_model(self.__class__)
super(Animal, self).save(*args, **kwargs)
def as_leaf_class(self):
content_type = self.content_type
model = content_type.model_class()
if model == Animal:
return self
return model.objects.get(id=self.id)
class Sheep(Animal):
wool = models.IntegerField()
objects = AnimalManager()
def __unicode__(self):
return 'Sheep: %s' % (self.name)
Testing:
>>> from animals.models import *
>>> Animal.objects.all()
[<Sheep: Sheep: White sheep>, <Animal: Animal: Dog>]
>>> s, d = Animal.objects.all()
>>> str(s)
'Sheep: White sheep'
>>> str(d)
'Animal: Dog'
>>>