Skip to main content

Polymorphic models implementation for django

Project description

A django application that provides a simple way to retrieve models type casted to their original ContentType.

Installation

>>> pip install polymodels

Make sure 'django.contrib.contenttypes' and 'polymodels' are in your INSTALLED_APPS:

INSTALLED_APPS += ('django.contrib.contenttypes', 'polymodels')

Usage

You subclass PolymorphicModel which is an abstract model class.

from django.db import models
from polymodels.models import PolymorphicModel

class Animal(PolymorphicModel):
    name = models.CharField(max_length=255)

    def __unicode__(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 ContentType 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 select_subclasses method:

>>> 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 select_subclass:

>>> 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 retreive original results by the select_subclasses call.

>>> Animal.objects.select_subclasses()
[<Animal: animal>, <Animal: mammal>, <Animal: reptile>, <Animal: snake>]

Each instance of PolymorphicModel has a type_cast method that knows how to convert itself to the correct ContentType.

>>> animal_snake = Animal.objects.get(pk=snake.pk)
<Animal: snake>
>>> animal_snake.type_cast()
<Snake: snake>
>>> animal_snake.type_cast(Reptile)
<Reptile: snake>

If the PolymorphicModel.content_type fields conflicts with one of your existing fields you just have to subclass polymodels.models.BasePolymorphicModel instead. Just don’t forget to indicates which field it should use instead by defining a content_type_field_name' attribute on you model. This field should be a ForeignKey to ContentType:

from django.contrib.contenttypes.models import ContentType
from django.db import models
from polymodels.models import BasePolymorphicModel

class MyModel(BasePolymorphicModel):
    content_type_field_name = 'polymorphic_ct'
    polymorphic_ct = models.ForeignKey(ContentType)

How it works

Under the hood select_subclasses calls seleted_related to avoid unnecessary queries and filter if you pass some classes to it. On querset 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.

Caution

Until #16572 it’s not possible to issue a select_related over multiple one-to-one relationships. For example, given the models defined above, Animal.objects.select_related('mammal__dog') would throw a strange TypeError. To avoid this issue, select_subclasses limits such lookups to one level depth.

Note of the author

I’m aware there’s already some projects tackling this issue, including django-polymorphic. However I wanted to try implementing this feature in a lightweight way: no __metaclass___ or __init__ overrides. Plus this was really just an extraction of django-mutant’s own mecanism of handling this since I needed it in another project.

Credits

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django-polymodels-1.0.0.tar.gz (8.2 kB view details)

Uploaded Source

File details

Details for the file django-polymodels-1.0.0.tar.gz.

File metadata

File hashes

Hashes for django-polymodels-1.0.0.tar.gz
Algorithm Hash digest
SHA256 125851a0413fb1035c3c546e18cd653688d5a46796f7f200b62c62be9f60b43b
MD5 070e71d71db3df5bcf10ec841d637b55
BLAKE2b-256 4a744de6f895624d5432210c4b6f2139d334dd5daeee08bbc3ad7925578c9cf3

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page