- Author:
- julkiewicz
- Posted:
- April 10, 2011
- Language:
- Python
- Version:
- 1.3
- Score:
- 1 (after 3 ratings)
An emulation of "table per hierarchy" a.k.a. "single table inheritance" in Django. The base class must hold all the fields. It's subclasses are not allowed to contain any additional fields and optimally they should be proxies. They however may provide additional methods to operate on the declared field. The presented solution supports implicit inheritance, even across ForeignKeys, and ManyToManyFields. No additional database hits are imposed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | from abc import abstractmethod
from django.db import models
from django.db.models import query
class PolymorphicQuerySet(query.QuerySet):
def get(self, *args, **kwargs):
if kwargs.has_key('model_class'):
cls = kwargs.pop('model_class')
kwargs['mod'] = cls.__module__
kwargs['cls'] = cls.__name__
return super(PolymorphicQuerySet, self).get(*args, **kwargs)
def filter(self, *args, **kwargs):
if kwargs.has_key('model_class'):
cls = kwargs.pop('model_class')
kwargs['mod'] = cls.__module__
kwargs['cls'] = cls.__name__
return super(PolymorphicQuerySet, self).filter(*args, **kwargs)
class PolymorphicManager(models.Manager):
def get_query_set(self):
return PolymorphicQuerySet(self.model, using=self._db)
def get(self, *args, **kwargs):
return self.get_query_set().get(*args, **kwargs)
def filter(self, *args, **kwargs):
return self.get_query_set().filter(*args, **kwargs)
class PolymorphicModel(models.Model):
classes = dict()
mod = models.CharField(max_length=50)
cls = models.CharField(max_length=30)
class Meta:
abstract = True
@staticmethod
def __new__(cls, *args, **kwargs):
if len(args) > len(cls._meta.fields):
raise IndexError("Number of args exceeds number of fields")
c = None
m = None
fields_iter = iter(cls._meta.fields)
for val, field in itertools.izip(args, fields_iter):
if field.name == 'cls':
c = val
elif field.name == 'mod':
m = val
if c is None:
c = kwargs.get('cls', None)
if m is None:
m = kwargs.get('mod', None)
if c is not None:
assert m is not None
m = str(m)
c = str(c)
if not Model.classes.has_key(c):
cls = getattr(__import__(m, globals(), locals(), [c]), c)
PolymorphicModel.classes[(m, c)] = cls
else:
cls = Model.classes[(m, c)]
return super(PolymorphicModel, cls).__new__(cls, *args, **kwargs)
def save(self, *args, **kwargs):
if not self.cls:
self.mod = self.__class__.__module__
self.cls = self.__class__.__name__
super(PolymorphicModel, self).save(*args, **kwargs)
class SomeBase(PolymorphicModel):
a = IntegerField(default=0)
b = IntegerField(default=0)
@abstractmethod
def something(self): pass
class SomeDerivedA(SomeBase):
class Meta:
proxy = True
def something(self):
self.a += 1
class SomeDerivedB(SomeBase):
class Meta:
proxy = True
def something(self):
self.b += 1
class SomeCollection(models.Model):
values = models.ManyToManyField(SomeBase)
>>> a = SomeDerivedA()
>>> a.save()
>>> b = SomeDerivedB()
>>> b.save()
>>> x = SomeCollection()
>>> x.values = [a, b]
>>> x.save()
>>> y = SomeCollection.objects.get(pk=x.pk)
>>> y.values.objects[0].something()
>>> y.values.objects[0].a
1
>>> y.values.objects[0].b
0
>>> y.values.objects[1].something()
>>> y.values.objects[1].a
0
>>> y.values.objects[1].b
1
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 3 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
Corrected a serious bug - using new instead of init to perform class changes.
#
I think there are some imports missing, one is itertools, the other is Model; the second I don't know where to import from.
#
Any suggestions on how to resolve his:
@facundo_olano already mentioned it. Is there a solution?
#
Please login first before commenting.