Skip to content

Commit

Permalink
Merge pull request #10 from vdboor/master
Browse files Browse the repository at this point in the history
Polymorphic admin interface
  • Loading branch information
chrisglass committed Aug 6, 2012
2 parents db4cc4d + bba8db1 commit 28b8885
Show file tree
Hide file tree
Showing 14 changed files with 776 additions and 12 deletions.
1 change: 1 addition & 0 deletions AUTHORS.rst
Expand Up @@ -12,3 +12,4 @@ Contributors
* Charles Leifer (python 2.4 compatibility)
* Germán M. Bravo
* Martin Brochhaus
* Diederik van der Boor (polymorphic admin interface)
6 changes: 6 additions & 0 deletions CHANGES.rst
Expand Up @@ -3,6 +3,12 @@
Changelog
++++++++++

Juli 5, 2012, Polymorphic admin interface
=========================================

Added a polymorphic admin interface. The admin interface is able to add polymorphic models,
and the admin edit screen also displays the custom fields of the polymorphic model.


2011-12-20 Renaming, refactoring, new maintainer
================================================
Expand Down
95 changes: 92 additions & 3 deletions DOCS.rst
Expand Up @@ -153,6 +153,98 @@ In the examples below, these models are being used::
field3 = models.CharField(max_length=10)


Using polymorphic models in the admin interface
-----------------------------------------------

Naturally, it's possible to register individual polymorphic models in the Django admin interface.
However, to use these models in a single cohesive interface, some extra base classes are available.

The polymorphic admin interface works in a simple way:

* The add screen gains an additional step where the desired child model is selected.
* The edit screen displays the admin interface of the child model.
* The list screen still displays all objects of the base class.

The polymorphic admin is implemented via a parent admin that forwards the *edit* and *delete* views
to the ``ModelAdmin`` of the derived child model. The *list* page is still implemented by the parent model admin.

Both the parent model and child model need to have a ``ModelAdmin`` class.
Only the ``ModelAdmin`` class of the parent/base model has to be registered in the Django admin site.

The parent model
~~~~~~~~~~~~~~~~

The parent model needs to inherit ``PolymorphicParentModelAdmin``, and implement the following:

* ``base_model`` should be set
* ``child_models`` should be set, or:

* ``get_child_models()`` should return a list with (Model, ModelAdmin) tuple.

The exact implementation can depend on the way your module is structured.
For simple inheritance situations, ``child_models`` is best suited.
For large applications, this leaves room for a plugin registration system.

The child models
~~~~~~~~~~~~~~~~

The admin interface of the derived models should inherit from ``PolymorphicChildModelAdmin``.
Again, ``base_model`` should be set in this class as well.
This class implements the following features:

* It corrects the breadcrumbs in the admin pages.
* It extends the template lookup paths, to look for both the parent model and child model in the ``admin/app/model/change_form.html`` path.
* It allows to set ``base_form`` so the derived class will automatically include other fields in the form.
* It allows to set ``base_fieldsets`` so the derived class will automatically display any extra fields.

By adding ``polymorphic`` to the ``INSTALLED_APPS``, the breadcrumbs will be
fixed as well, to stay the same for all child models.

The standard ``ModelAdmin`` attributes ``form`` and ``fieldsets`` should rather be avoided at the base class,
because it will hide any additional fields which are defined in the derived model. Instead,
use the ``base_form`` and ``base_fieldsets`` instead. The ``PolymorphicChildModelAdmin`` will
automatically detect the additional fields that the child model has, display those in a separate fieldset.


Example
~~~~~~~

::

from django.contrib import admin
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin


class ModelAChildAdmin(PolymorphicChildModelAdmin):
""" Base admin class for all child models """
base_model = ModelA

# By using these `base_...` attributes instead of the regular ModelAdmin `form` and `fieldsets`,
# the additional fields of the child models are automatically added to the admin form.
base_form = ...
base_fieldsets = (
...
)

class ModelBAdmin(ModelAChildAdmin):
# define custom features here

class ModelCAdmin(ModelBAdmin):
# define custom features here


class ModelAParentAdmin(PolymorphicParentModelAdmin):
""" The parent model admin """
base_model = ModelA
child_models = (
(ModelB, ModelBAdmin),
(ModelC, ModelCAdmin),
}

# Only the parent needs to be registered:
admin.site.register(ModelA, ModelAParentAdmin)


Filtering for classes (equivalent to python's isinstance() ):
-------------------------------------------------------------

Expand Down Expand Up @@ -504,9 +596,6 @@ Restrictions & Caveats
``extra()`` has one restriction: the resulting objects are required to have
a unique primary key within the result set.

* Django Admin Integration: There currently is no specific admin integration,
but it would most likely make sense to have one.

* Diamond shaped inheritance: There seems to be a general problem
with diamond shaped multiple model inheritance with Django models
(tested with V1.1 - V1.3).
Expand Down
2 changes: 1 addition & 1 deletion MANIFEST.in
Expand Up @@ -2,4 +2,4 @@ include README.rst
include LICENSE
include DOCS.rst
include CHANGES.rst

recursive-include polymorphic/templates/ *.html
63 changes: 63 additions & 0 deletions pexp/admin.py
@@ -0,0 +1,63 @@
from django.contrib import admin
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin
from pexp.models import *


class ProjectChildAdmin(PolymorphicChildModelAdmin):
base_model = Project

class ProjectAdmin(PolymorphicParentModelAdmin):
base_model = Project
child_models = (
(Project, ProjectChildAdmin),
(ArtProject, ProjectChildAdmin),
(ResearchProject, ProjectChildAdmin),
)

admin.site.register(Project, ProjectAdmin)



class ModelAChildAdmin(PolymorphicChildModelAdmin):
base_model = ModelA

class ModelAAdmin(PolymorphicParentModelAdmin):
base_model = ModelA
child_models = (
(ModelA, ModelAChildAdmin),
(ModelB, ModelAChildAdmin),
(ModelC, ModelAChildAdmin),
)

admin.site.register(ModelA, ModelAAdmin)


if 'Model2A' in globals():
class Model2AChildAdmin(PolymorphicChildModelAdmin):
base_model = Model2A

class Model2AAdmin(PolymorphicParentModelAdmin):
base_model = Model2A
child_models = (
(Model2A, Model2AChildAdmin),
(Model2B, Model2AChildAdmin),
(Model2C, Model2AChildAdmin),
)

admin.site.register(Model2A, Model2AAdmin)


if 'UUIDModelA' in globals():
class UUIDModelAChildAdmin(PolymorphicChildModelAdmin):
base_model = UUIDModelA

class UUIDModelAAdmin(PolymorphicParentModelAdmin):
base_model = UUIDModelA
child_models = (
(UUIDModelA, UUIDModelAChildAdmin),
(UUIDModelB, UUIDModelAChildAdmin),
(UUIDModelC, UUIDModelAChildAdmin),
)

admin.site.register(UUIDModelA, UUIDModelAAdmin)

0 comments on commit 28b8885

Please sign in to comment.