Created
September 13, 2012 21:50
-
-
Save cridenour/3717953 to your computer and use it in GitHub Desktop.
Polymorphic Django Models
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.db import models | |
from django.db.models.base import ModelBase | |
from django.db.models.query import QuerySet | |
from django.contrib.contenttypes.models import ContentType | |
class PolymorphicMetaclass(ModelBase): | |
def __new__(cls, name, bases, dct): | |
def save(self, *args, **kwargs): | |
if(not self.content_type): | |
self.content_type = ContentType.objects.get_for_model(self.__class__) | |
models.Model.save(self, *args, **kwargs) | |
def downcast(self): | |
model = self.content_type.model_class() | |
if (model == self.__class__): | |
return self | |
return model.objects.get(id=self.id) | |
if issubclass(dct.get('__metaclass__', type), PolymorphicMetaclass): | |
dct['content_type'] = models.ForeignKey(ContentType, editable=False, null=True) | |
dct['save'] = save | |
dct['downcast'] = downcast | |
return super(PolymorphicMetaclass, cls).__new__(cls, name, bases, dct) | |
class DowncastMetaclass(PolymorphicMetaclass): | |
def __new__(cls, name, bases, dct): | |
dct['objects'] = DowncastManager() | |
return super(DowncastMetaclass, cls).__new__(cls, name, bases, dct) | |
class DowncastManager(models.Manager): | |
def get_query_set(self): | |
return DowncastQuerySet(self.model) | |
class DowncastQuerySet(QuerySet): | |
def __getitem__(self, k): | |
result = super(DowncastQuerySet, self).__getitem__(k) | |
if isinstance(result, models.Model) : | |
return result.downcast() | |
else : | |
return result | |
def __iter__(self): | |
for item in super(DowncastQuerySet, self).__iter__(): | |
yield item.downcast() | |
# Using them | |
class Content(models.Model): | |
__metaclass__ = DowncastMetaclass | |
owner = models.ForeignKey(User, related_name='owned_content') | |
title = models.TextField(max_length=255) | |
class AudioFile(Content): | |
external_file = models.FileField(upload_to='audio',editable=False,help_text='Should be a m3u8 playlist file for now.') | |
def __unicode__(self): | |
return self.title | |
def save(self, force_insert=False, force_update=False, using=None): | |
# I want to do something here but I can't figure out how. | |
return super(AudioFile, self).save(force_insert, force_update, using) |
If I'm understanding what you're trying to do correctly, you're much better of using an abstract base class.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I want to be able to use the model's save function on line 60, but have it be a DowncastMetaclass instanced model. On line 11 I am calling
models.Model.save
but am having a hard time wrapping my head around how to call AudioFile's save function after that (or before that I guess). I'd like to clean this up (sourced from a no longer maintained project) and have it be completely drop-in.