Django Polymorphic
Django Polymorphic simplifies the use of inherited models in Django projects by automatically returning subclass instances when querying a base model. It enhances Django's multi-table inheritance to provide a seamless polymorphic experience in the ORM and admin interface. The library is actively maintained by the Jazzband community and is currently at version 4.11.2, with regular updates addressing bugs and adding features like comprehensive type annotations.
Warnings
- gotcha Query performance can degrade with many distinct subclasses. Each unique subclass in a queryset requires an additional `INNER JOIN` query to fetch its specific fields, leading to `1 + N` queries (where N is the number of distinct subclasses) for a single `all()` call. While better than `N` queries, it's a consideration for very complex hierarchies.
- breaking The `drf-polymorphic` package was merged directly into `django-polymorphic` starting with version 4.10.0. This means import paths for `PolymorphicSerializer` and related components have changed.
- gotcha Methods like `values()` and `values_list()` do not return polymorphic results. They will return fields only from the base model, or specific fields if explicitly requested, but will not perform downcasting to subclasses.
- gotcha When using `dumpdata` with polymorphic models, you must include the `--natural-primary` and `--natural-foreign` flags. Polymorphic models rely on Django's `ContentType` framework, and these flags ensure correct serialization and deserialization across different database instances.
- deprecated The keyword argument `polymorphic=False` for disabling polymorphic behavior in queries is no longer supported.
- gotcha In admin integration, when defining a `PolymorphicChildModelAdmin` that might be extended by further derived classes, use `base_form` and `base_fieldsets` attributes instead of the standard `form` and `fieldsets`. This ensures that additional fields from further child models are automatically added correctly.
Install
-
pip install django-polymorphic
Imports
- PolymorphicModel
from polymorphic.models import PolymorphicModel
- PolymorphicParentModelAdmin
from polymorphic.admin import PolymorphicParentModelAdmin
- PolymorphicChildModelAdmin
from polymorphic.admin import PolymorphicChildModelAdmin
- PolymorphicManager
from polymorphic.managers import PolymorphicManager
- PolymorphicSerializer
from polymorphic.contrib.rest_framework.serializers import PolymorphicSerializer
Quickstart
import os
import django
from django.conf import settings
from django.db import models
# Minimal Django setup for runnable example
settings.configure(
INSTALLED_APPS=[
'django.contrib.auth',
'django.contrib.contenttypes',
'polymorphic',
__name__ # For models to be registered
],
DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:'}}
)
django.setup()
from polymorphic.models import PolymorphicModel
# Define your polymorphic models
class Project(PolymorphicModel):
topic = models.CharField(max_length=90)
def __str__(self):
return f"{self.__class__.__name__}: {self.topic}"
class ArtProject(Project):
artist = models.CharField(max_length=90)
def __str__(self):
return f"{self.__class__.__name__}: {self.topic} by {self.artist}"
class ResearchProject(Project):
supervisor = models.CharField(max_length=90)
def __str__(self):
return f"{self.__class__.__name__}: {self.topic} supervised by {self.supervisor}"
# Create database schema
from django.core.management import call_command
call_command('makemigrations', __name__, interactive=False)
call_command('migrate', interactive=False)
# Create objects
Project.objects.create(topic="Department Party")
ArtProject.objects.create(topic="Painting with Tim", artist="T. Turner")
ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter")
# Query the base model to get polymorphic results
all_projects = Project.objects.all()
print("All Projects:")
for project in all_projects:
print(project)
# Filter by subclass type
art_projects = Project.objects.instance_of(ArtProject)
print("\nArt Projects:")
for project in art_projects:
print(project)