Accessing Inherited Models from the Parent in Django
One of the neat features of Django's ORM is Model inheritance (table-level). It allows several neat data design patterns to occur. Here's an example. Let's say we're developing a website for a game company. The company sells two types of products: board games and video games. All of the products will share some data in common, name and product_id for example, but we also need to store specific details about each. Using model inheritance we can do something as follows.
class Product(models.Model):In a real use-case scenario you'd most likely have more than 1 field per, but for this example I wanted to keep things simple.
name = models.CharField(max_length=75)
product_id = models.SmallIntegerField()
price = models.DecimalField()
class BoardGame(Product):
num_of_players = models.SmallIntegerField()
game_type = models.CharField(max_length=50)
class VideoGame(Product):
PLATFORM_CHOICES = (
('wii', 'Wii),
('xb3', 'Xbox 360'),
('ps3', 'Playstation 3'),
)
platform = models.CharField(max_length=3, choices=PLATFORM_CHOICES)
The way Django implements this, if you were to query one of the child models, you'd be able to access the methods from the parent models...
b = BoardGame.objects.all()[1]Another thing that's cool is child instances have a parent instance record. Using the "Djangopoloy" game from above, which is technically type BoardGame, one could still query Product and retrieve it.
print b.name
>>> 'Djangopoly'
p = Product.objects.get(name='Djangopoly')This is really useful, but sometimes you need to go the opposite direction, and this is where Django's implementation stops. The link can't go from a Product model instance to a BoardGame. It can't retrieve state as if it was of type BoardGame.
print p.platformBecause the need for this seems to be arising more often than not lately for me, I put together a re-usbale bit of code to overcome this limitation. I'll post the code below (a GitHub gist), but using it is actually quite simple.
>>> CAN'T DO THAT!
It works by providing an abstract model that the parent model inherits from instead of models.Model:
from inheritance.models import ChildAwareModelThen, an inner class "Inheritance" is supplied to describe children of the model.
class Product(ChildAwareModel):
...
pass
class Product(ChildAwareModel):Only children that need to be reversed to should be set. Once that is configured, a method "get_child_model()" will become available, and can be used like so:
...
class Inheritance:
children = (
'myapp.models.BoardGame',
'myapp.mdoels.VideoGame',
)
p = Product.objects.get(name='Djangopoly')I'm finding this particularly useful when I've created an aggregate type page -- that is a page that shows a summary of all the generic types (Product) -- but need to on user-click show them some type of product-specific detail.
b = p.get_child_model()
print b.num_of_players
>>> 4
The implementation for ChildAwareModel is below. Save it somewhere on your python path and enjoy. :)
ChildAwareModel Gist