Skip to content

Instantly share code, notes, and snippets.

@cridenour
Created September 13, 2012 21:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cridenour/3717953 to your computer and use it in GitHub Desktop.
Save cridenour/3717953 to your computer and use it in GitHub Desktop.
Polymorphic Django Models
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)
@cridenour
Copy link
Author

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.

@BrianHicks
Copy link

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