Created
September 13, 2012 21:50
-
-
Save cridenour/3717953 to your computer and use it in GitHub Desktop.
Polymorphic Django Models
This file contains hidden or 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) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
If I'm understanding what you're trying to do correctly, you're much better of using an abstract base class.