Lets say I have model inheritance set up in the way defined below.
class ArticleBase(models.Model):
title = models.CharField()
author = models.CharField()
class Review(ArticleBase):
rating = models.IntegerField()
class News(ArticleBase):
source = models.CharField()
If I need a list of all articles regardless of type (in this case both Reviews and News) ordered by title, I can run a query on ArticleBase. Is there an easy way once I have an ArticleBase record to determine if it relates to a Review or a News record without querying both models to see which has the foreign key of the record I am on?
I am assuming that all ArticleBase instances are instances of ArticleBase subclasses.
One solution is to store the subclass name in ArticleBase and some methods that return the subclass or subclass object based on that information. As multi-table inheritance defines a property on the parent instance to access a child instance, this is all pretty straight forward.
from django.db import models
class ArticleBase(models.Model):
title = models.CharField()
author = models.CharField()
# Store the actual class name.
class_name = models.CharField()
# Define save to make sure class_name is set.
def save(self, *args, **kwargs):
self.class_name = self.__class__.__name__
super(ArticleBase, self).save(*args, **kwargs)
# Multi-table inheritance defines an attribute to fetch the child
# from a parent instance given the lower case subclass name.
def get_child(self):
return getattr(self, self.class_name.lower())
# If indeed you really need the class.
def get_child_class(self):
return self.get_child().__class__
# Check the type against a subclass name or a subclass.
# For instance, 'if article.child_is(News):'
# or 'if article.child_is("News"):'.
def child_is(self, cls):
if isinstance(cls, basestring):
return cls.lower() == self.class_name.lower()
else:
return self.get_child_class() == cls
class Review(ArticleBase):
rating = models.IntegerField()
class News(ArticleBase):
source = models.CharField()
This is by no means the only way to go about this. It is, however, a pretty simple and straight forward solution. The excellent contrib contenttypes app and the generic module which leverages it offer a wealth of ways to do this, well, generically.
It could be useful to have the following in ArticleBase:
def __unicode__(self)
return self.get_child().__unicode__()
In that case, be aware that failure to define __unicode__ in the subclasses, or calling __unicode__ on an instance of ArticleBase (one that has not been subclassed) would lead to an infinite recursion. Thus the admonition below re sanity checking (for instance, preventing just such an instantiation of ArticleBase directly).
Disclaimer:
This code is untested, I'm sure I've got a typo or two in there, but the basic concept should be sound. Production level code should probably have some sanity checking to intercept usage errors.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With