In the Lifestream service I’ve been working on, I presented myself with the need to have some class abstraction, but not in the fashion which is available in Django models. I wanted to achieve a base class, which is stored in the database, and then child classes which simply override some methods. It turned out this would be a lot more complex than anticipated.
This is a fairly common approach to class abstraction, especially in Python. It’s used all over the place in your daily code. Whether you are overriding the save() method on your model, or creating your own admin form by subclassing ModelAdmin. I wanted to accomplish a similar task, but doing so on a model. Let’s call it backwards abstraction (since abstraction works the other direction in Django). We create a single table, which houses many classes, and possibly even some denormalization for additional data on these classes.
class Source(TemplateModel): id = UUIDField(auto=True, primary_key=True) plugin = models.CharField(max_length=32) title = models.CharField(max_length=64) ... options = JSONField(blank=True, null=True, editable=False) # Tell the TemplateModel class what we have called our field. __template_key__ = 'plugin' def render(self): raise NotImplementedError
As you can see in the above, we have our base class. It’s extending from the TemplateModel class (more on this further in), and contains some basic information. We have a plugin field which is the name of the class which is extending the instance, and an options field for storing some extra information based on that class. There is also a render() method for the extension which should be handled by the subclass.
class TwitterExtension(FeedExtension): def render(self, event): return event.title
Our Twitter extension, for this sample, is very simple. All it does it say “Use the base Source class, and render the title of the event”. Now while this may not seem all that useful, it begins to be when we do even more complex development. To backtrack things a bit, TwitterExtension extends from the FeedExtension, which is where we are handling most of the logic.
class FeedExtension(Source): def render(self, event): return '<a href="%s">%s</a>' % (event.url, event.title) def fetch_url(self, url): feed = feedparser.parse(url) for entry in feed['entries']: data = self.fetch_entry(entry) data['key'] = data.get('key', self.get_media_type_for_url(url)) data['signature'] = self.get_signature_for_entry(entry) yield data
As you can see here, we’re simply changing how the Twitter events are rendered. So simple example, complex to build. Even more so, we want these classes to automatically be delivered whenever we access the base Source class, as well as being able to use the classes directly.
# The instance becomes a <TwitterExtension: TwitterExtensionobject> on creation. twitter = Source.objects.create(plugin='TwitterExtension') # And it saves into the Source model. twitter.save()
To do this I created a class which I’ve randomly described as TemplateModel. It does exactly what is talked about above. It stores references to each child class within the parent, and upon instantiation, if it can, it returns the child class instead of the parent.
So without further delay, view the source for TemplateModel.
I’d be interested in hearing if anyone else has come up with their own solutions, and if you use something like this in your project how well its working for you.

View Comments Responses to "Denormalizing Model Abstraction in Django"
This an interesting way to solve this problem, but I balk at the use of the term “denormalization” to describe what is going on. That word sets up expectations about the problem we are trying to solve that is at odds with what this mechanism actually provides.
I am searching for a better word and the closest I have come up with so far is “thunking” as the mechanism is akin to actualizing virtual function calling properties for Django models. (Also the implementation mechanism is sufficiently low-level to deserve the “thunking” label.) But I will bet that someone can suggest an even better name for this particular mechanism. Ideally it would be a fairly unique name so that searches can find it easily.
I am also looking forward to seeing if some other Django expert can suggest a cleaner and simpler way to implement a solution for this particular use case that offers the same power and flexibility. I wouldn’t be surprised if the answer is no.
One final note – I also balk a bit at the use of the term TemplateModel as that term overloads the word Template in the Django context. I suggest ExtensibleBaseModel instead.
This is too powerful to fall on untrained hands…
I agree about the TemplateModel, it made me think of Django Templates
We solved a similar problem using actual plugins and then delegating method implementations instead of trying to get fancy with subclasses. The primary class has core behaviors and knows how to find a plugin based on a name. The plugin is a separate object, instantiated by the primary class and invoked as part of the implementation of certain methods.
Well we actually use Sql alchemy, which makes it very easy to achieve. Running two orms is sometimes annoying overhead though.
Leave A Reply