################# django-polymodels #################
A django application that provides a simple way to retrieve models type casted
to their original
pip install django-polymodels
'polymodels' are in
INSTALLED_APPS += ('django.contrib.contenttypes', 'polymodels')
PolymorphicModel, an abstract model class.
from django.db import models from polymodels.models import PolymorphicModel class Animal(PolymorphicModel): name = models.CharField(max_length=255) def __str__(self): return self.name class Mammal(Animal): pass class Dog(Mammal): pass class Reptile(Animal): pass class Snake(Reptile): class Meta: proxy = True
Objects are created the same way as usual and their associated
is saved automatically:
animal = Animal.objects.create(name='animal') mammal = Mammal.objects.create(name='mammal') reptile = Reptile.objects.create(name='reptile') snake = Snake.objects.create(name='snake')
To retreive type casted instances from the
Animal.objects manager you just
have to use the
Animal.objects.select_subclasses() [<Animal: animal>, <Mammal: mammal>, <Reptile: reptile>, <Snake: snake>]
You can also retreive a subset of the subclasses by passing them as arguments to
Animal.objects.select_subclasses(Reptile) [<Reptile: reptile>, <Snake: snake>]
Or directly from subclasses managers.
Reptile.objects.select_subclasses(Snake) [<Snake: snake>]
Note that you can also retrieve original results by avoiding the
Animal.objects.all() [<Animal: animal>, <Animal: mammal>, <Animal: reptile>, <Animal: snake>]
It's also possible to select only instances of the model to which the
manager is attached by using the
Mammal.objects.all() [<Mammal: mammal>]
Each instance of
PolymorphicModel has a
type_cast method that knows how
to convert itself to the correct
animal_snake = Animal.objects.get(pk=snake.pk) <Animal: snake> animal_snake.type_cast() <Snake: snake> animal_snake.type_cast(Reptile) <Reptile: snake>
PolymorphicModel.content_type fields conflicts with one of your
existing fields you just have to subclass
polymodels.models.BasePolymorphicModel and specify which field polymodels
should use instead by defining a
CONTENT_TYPE_FIELD attribute on your model.
This field must be a
from django.contrib.contenttypes.models import ContentType from django.db import models from polymodels.models import BasePolymorphicModel class MyModel(BasePolymorphicModel): CONTENT_TYPE_FIELD = 'polymorphic_ct' polymorphic_ct = models.ForeignKey(ContentType)
How it works
Under the hood
seleted_related to avoid
unnecessary queries and
filter if you pass some classes to it. On queryset
iteration, the fetched instanced are converted to their correct type by calling
BasePolymorphicModel.type_cast. Note that those lookups are cached on class
creation to avoid computing them on every single query.
Note of the author
I'm aware there's already plenty of existing projects tackling the whole
model-inheritance-type-casting-thing such as
I wanted to implement this feature in a lightweight way: no
__init__ overrides while using django's public API as
much as possible. In the end, this was really just an extraction of
django-mutant's own mecanism of handling this since I needed it as a
standalone app for another project.
If you happen to encounter a bug or would like to suggest a feature addition
file an issue or
create a pull request containing tests.
.. _file an issue: https://github.com/charettes/django-polymodels/issues .. _create a pull request: https://github.com/charettes/django-polymodels/pulls
post of Jeff Elmores_.
.. _post of Jeff Elmores: http://jeffelmore.org/2010/11/11/automatic-downcasting-of-inherited-models-in-django/