1Third-party applications support
2================================
3
4
5django-guardian support
6-----------------------
7
8.. versionadded:: 1.0.2
9
10You can configure django-guardian_ to use the base model for object level permissions.
11Add this option to your settings:
12
13.. code-block:: python
14
15    GUARDIAN_GET_CONTENT_TYPE = 'polymorphic.contrib.guardian.get_polymorphic_base_content_type'
16
17This option requires django-guardian_ >= 1.4.6. Details about how this option works are available in the
18`django-guardian documentation <https://django-guardian.readthedocs.io/en/latest/configuration.html#guardian-get-content-type>`_.
19
20
21django-rest-framework support
22-----------------------------
23
24The django-rest-polymorphic_ package provides polymorphic serializers that help you integrate your polymorphic models with `django-rest-framework`.
25
26
27Example
28~~~~~~~
29
30Define serializers:
31
32.. code-block:: python
33
34    from rest_framework import serializers
35    from rest_polymorphic.serializers import PolymorphicSerializer
36    from .models import Project, ArtProject, ResearchProject
37
38
39    class ProjectSerializer(serializers.ModelSerializer):
40        class Meta:
41            model = Project
42            fields = ('topic', )
43
44
45    class ArtProjectSerializer(serializers.ModelSerializer):
46        class Meta:
47            model = ArtProject
48            fields = ('topic', 'artist')
49
50
51    class ResearchProjectSerializer(serializers.ModelSerializer):
52        class Meta:
53            model = ResearchProject
54            fields = ('topic', 'supervisor')
55
56
57    class ProjectPolymorphicSerializer(PolymorphicSerializer):
58        model_serializer_mapping = {
59            Project: ProjectSerializer,
60            ArtProject: ArtProjectSerializer,
61            ResearchProject: ResearchProjectSerializer
62        }
63
64Create viewset with serializer_class equals to your polymorphic serializer:
65
66.. code-block:: python
67
68    from rest_framework import viewsets
69    from .models import Project
70    from .serializers import ProjectPolymorphicSerializer
71
72
73    class ProjectViewSet(viewsets.ModelViewSet):
74        queryset = Project.objects.all()
75        serializer_class = ProjectPolymorphicSerializer
76
77
78django-extra-views
79------------------
80
81.. versionadded:: 1.1
82
83The :mod:`polymorphic.contrib.extra_views` package provides classes to display polymorphic formsets
84using the classes from django-extra-views_. See the documentation of:
85
86* :class:`~polymorphic.contrib.extra_views.PolymorphicFormSetView`
87* :class:`~polymorphic.contrib.extra_views.PolymorphicInlineFormSetView`
88* :class:`~polymorphic.contrib.extra_views.PolymorphicInlineFormSet`
89
90
91django-mptt support
92-------------------
93
94Combining polymorphic with django-mptt_ is certainly possible, but not straightforward.
95It involves combining both managers, querysets, models, meta-classes and admin classes
96using multiple inheritance.
97
98The django-polymorphic-tree_ package provides this out of the box.
99
100
101django-reversion support
102------------------------
103
104Support for django-reversion_ works as expected with polymorphic models.
105However, they require more setup than standard models. That's become:
106
107* Manually register the child models with django-reversion_, so their ``follow`` parameter can be set.
108* Polymorphic models use `multi-table inheritance <https://docs.djangoproject.com/en/dev/topics/db/models/#multi-table-inheritance>`_.
109  See the `reversion documentation <https://django-reversion.readthedocs.io/en/latest/api.html#multi-table-inheritance>`_
110  how to deal with this by adding a ``follow`` field for the primary key.
111* Both admin classes redefine ``object_history_template``.
112
113
114Example
115~~~~~~~
116
117The admin :ref:`admin example <admin-example>` becomes:
118
119.. code-block:: python
120
121    from django.contrib import admin
122    from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin
123    from reversion.admin import VersionAdmin
124    from reversion import revisions
125    from .models import ModelA, ModelB, ModelC
126
127
128    class ModelAChildAdmin(PolymorphicChildModelAdmin, VersionAdmin):
129        base_model = ModelA  # optional, explicitly set here.
130        base_form = ...
131        base_fieldsets = (
132            ...
133        )
134
135    class ModelBAdmin(ModelAChildAdmin, VersionAdmin):
136        # define custom features here
137
138    class ModelCAdmin(ModelBAdmin):
139        # define custom features here
140
141
142    class ModelAParentAdmin(VersionAdmin, PolymorphicParentModelAdmin):
143        base_model = ModelA  # optional, explicitly set here.
144        child_models = (
145            (ModelB, ModelBAdmin),
146            (ModelC, ModelCAdmin),
147        )
148
149    revisions.register(ModelB, follow=['modela_ptr'])
150    revisions.register(ModelC, follow=['modelb_ptr'])
151    admin.site.register(ModelA, ModelAParentAdmin)
152
153Redefine a :file:`admin/polymorphic/object_history.html` template, so it combines both worlds:
154
155.. code-block:: html+django
156
157    {% extends 'reversion/object_history.html' %}
158    {% load polymorphic_admin_tags %}
159
160    {% block breadcrumbs %}
161        {% breadcrumb_scope base_opts %}{{ block.super }}{% endbreadcrumb_scope %}
162    {% endblock %}
163
164This makes sure both the reversion template is used, and the breadcrumb is corrected for the polymorphic model.
165
166.. _django-reversion-compare-support:
167
168django-reversion-compare support
169--------------------------------
170
171The django-reversion-compare_ views work as expected, the admin requires a little tweak.
172In your parent admin, include the following method:
173
174.. code-block:: python
175
176    def compare_view(self, request, object_id, extra_context=None):
177        """Redirect the reversion-compare view to the child admin."""
178        real_admin = self._get_real_admin(object_id)
179        return real_admin.compare_view(request, object_id, extra_context=extra_context)
180
181As the compare view resolves the the parent admin, it uses it's base model to find revisions.
182This doesn't work, since it needs to look for revisions of the child model. Using this tweak,
183the view of the actual child model is used, similar to the way the regular change and delete views are redirected.
184
185
186.. _django-extra-views: https://github.com/AndrewIngram/django-extra-views
187.. _django-guardian: https://github.com/django-guardian/django-guardian
188.. _django-mptt: https://github.com/django-mptt/django-mptt
189.. _django-polymorphic-tree: https://github.com/django-polymorphic/django-polymorphic-tree
190.. _django-rest-polymorphic: https://github.com/apirobot/django-rest-polymorphic
191.. _django-reversion-compare: https://github.com/jedie/django-reversion-compare
192.. _django-reversion: https://github.com/etianen/django-reversion
193