Third-party applications support

django-guardian support

New in version 1.0.2.

You can configure django-guardian to use the base model for object level permissions. Add this option to your settings:

GUARDIAN_GET_CONTENT_TYPE = 'polymorphic.contrib.guardian.get_polymorphic_base_content_type'

This option requires django-guardian >= 1.4.6. Details about how this option works are available in the django-guardian documentation.

django-rest-framework support

The django-rest-polymorphic package provides polymorphic serializers that help you integrate your polymorphic models with django-rest-framework.

Example

Define serializers:

from rest_framework import serializers
from rest_polymorphic.serializers import PolymorphicSerializer
from .models import Project, ArtProject, ResearchProject


class ProjectSerializer(serializers.ModelSerializer):
    class Meta:
        model = Project
        fields = ('topic', )


class ArtProjectSerializer(serializers.ModelSerializer):
    class Meta:
        model = ArtProject
        fields = ('topic', 'artist')


class ResearchProjectSerializer(serializers.ModelSerializer):
    class Meta:
        model = ResearchProject
        fields = ('topic', 'supervisor')


class ProjectPolymorphicSerializer(PolymorphicSerializer):
    model_serializer_mapping = {
        Project: ProjectSerializer,
        ArtProject: ArtProjectSerializer,
        ResearchProject: ResearchProjectSerializer
    }

Create viewset with serializer_class equals to your polymorphic serializer:

from rest_framework import viewsets
from .models import Project
from .serializers import ProjectPolymorphicSerializer


class ProjectViewSet(viewsets.ModelViewSet):
    queryset = Project.objects.all()
    serializer_class = ProjectPolymorphicSerializer

django-extra-views

New in version 1.1.

The polymorphic.contrib.extra_views package provides classes to display polymorphic formsets using the classes from django-extra-views. See the documentation of:

django-mptt support

Combining polymorphic with django-mptt is certainly possible, but not straightforward. It involves combining both managers, querysets, models, meta-classes and admin classes using multiple inheritance.

The django-polymorphic-tree package provides this out of the box.

django-reversion support

Support for django-reversion works as expected with polymorphic models. However, they require more setup than standard models. That’s become:

Example

The admin admin example becomes:

from django.contrib import admin
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin
from reversion.admin import VersionAdmin
from reversion import revisions
from .models import ModelA, ModelB, ModelC


class ModelAChildAdmin(PolymorphicChildModelAdmin, VersionAdmin):
    base_model = ModelA  # optional, explicitly set here.
    base_form = ...
    base_fieldsets = (
        ...
    )

class ModelBAdmin(ModelAChildAdmin, VersionAdmin):
    # define custom features here

class ModelCAdmin(ModelBAdmin):
    # define custom features here


class ModelAParentAdmin(VersionAdmin, PolymorphicParentModelAdmin):
    base_model = ModelA  # optional, explicitly set here.
    child_models = (
        (ModelB, ModelBAdmin),
        (ModelC, ModelCAdmin),
    )

revisions.register(ModelB, follow=['modela_ptr'])
revisions.register(ModelC, follow=['modelb_ptr'])
admin.site.register(ModelA, ModelAParentAdmin)

Redefine a admin/polymorphic/object_history.html template, so it combines both worlds:

{% extends 'reversion/object_history.html' %}
{% load polymorphic_admin_tags %}

{% block breadcrumbs %}
    {% breadcrumb_scope base_opts %}{{ block.super }}{% endbreadcrumb_scope %}
{% endblock %}

This makes sure both the reversion template is used, and the breadcrumb is corrected for the polymorphic model.

django-reversion-compare support

The django-reversion-compare views work as expected, the admin requires a little tweak. In your parent admin, include the following method:

def compare_view(self, request, object_id, extra_context=None):
    """Redirect the reversion-compare view to the child admin."""
    real_admin = self._get_real_admin(object_id)
    return real_admin.compare_view(request, object_id, extra_context=extra_context)

As the compare view resolves the the parent admin, it uses it’s base model to find revisions. This doesn’t work, since it needs to look for revisions of the child model. Using this tweak, the view of the actual child model is used, similar to the way the regular change and delete views are redirected.