Skip to content

Instantly share code, notes, and snippets.

@cridenour
Created Sep 13, 2012
Embed
What would you like to do?
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

This comment has been minimized.

Copy link
Owner Author

@cridenour cridenour commented Sep 13, 2012

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

This comment has been minimized.

Copy link

@BrianHicks BrianHicks commented Sep 14, 2012

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