docs start for formset/inline support

fix_request_path_info
Diederik van der Boor 2016-06-13 10:18:18 +02:00
parent a07ce7260c
commit 8c42893abd
8 changed files with 48 additions and 9 deletions

View File

@ -57,10 +57,12 @@ use the ``base_form`` and ``base_fieldsets`` instead. The ``PolymorphicChildMode
automatically detect the additional fields that the child model has, display those in a separate fieldset. automatically detect the additional fields that the child model has, display those in a separate fieldset.
Polymorphic Inlines Using polymorphic models in standard inlines
------------------- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To add a polymorphic child model as an Inline for another model, add a field to the inline's readonly_fields list formed by the lowercased name of the polymorphic parent model with the string "_ptr" appended to it. Otherwise, trying to save that model in the admin will raise an AttributeError with the message "can't set attribute". To add a polymorphic child model as an Inline for another model, add a field to the inline's ``readonly_fields`` list
formed by the lowercased name of the polymorphic parent model with the string ``_ptr`` appended to it.
Otherwise, trying to save that model in the admin will raise an AttributeError with the message "can't set attribute".
.. _admin-example: .. _admin-example:

12
docs/formsets.rst 100644
View File

@ -0,0 +1,12 @@
Formsets
========
Polymorphic models can be used in formsets.
Use the :func:`polymorphic.formsets.polymorphic_inlineformset_factory` function to generate the formset.
As extra parameter, the factory needs to know how to display the child models.
Provide a list of :class:`polymorphic.formsets.PolymorphicFormSetChild` objects for this
.. code-block:: python
from polymorphic.formsets import polymorphic_child_forms_factory

View File

@ -49,6 +49,7 @@ Getting started
quickstart quickstart
admin admin
formsets
performance performance
Advanced topics Advanced topics

View File

@ -6,8 +6,10 @@ from django.core.urlresolvers import resolve
from django.utils import six from django.utils import six
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from .helpers import PolymorphicInlineSupportMixin
class PolymorphicChildModelAdmin(admin.ModelAdmin):
class PolymorphicChildModelAdmin(PolymorphicInlineSupportMixin, admin.ModelAdmin):
""" """
The *optional* base class for the admin interface of derived models. The *optional* base class for the admin interface of derived models.

View File

@ -7,6 +7,12 @@ class PolymorphicChildModelFilter(admin.SimpleListFilter):
""" """
An admin list filter for the PolymorphicParentModelAdmin which enables An admin list filter for the PolymorphicParentModelAdmin which enables
filtering by its child models. filtering by its child models.
This can be used in the parent admin:
.. code-block:: python
list_filter = (PolymorphicChildModelFilter,)
""" """
title = _('Type') title = _('Type')
parameter_name = 'polymorphic_ctype' parameter_name = 'polymorphic_ctype'

View File

@ -5,7 +5,7 @@ This makes sure that admin fieldsets/layout settings are exported to the templat
""" """
from django.contrib.admin.helpers import InlineAdminFormSet, InlineAdminForm from django.contrib.admin.helpers import InlineAdminFormSet, InlineAdminForm
from ..formsets import BasePolymorphicModelFormSet from polymorphic.formsets import BasePolymorphicModelFormSet
class InlinePolymorphicAdminForm(InlineAdminForm): class InlinePolymorphicAdminForm(InlineAdminForm):
@ -75,6 +75,14 @@ class InlinePolymorphicAdminFormSet(InlineAdminFormSet):
class PolymorphicInlineSupportMixin(object): class PolymorphicInlineSupportMixin(object):
""" """
A Mixin to add to the regular admin, so it can work with our polymorphic inlines. A Mixin to add to the regular admin, so it can work with our polymorphic inlines.
This mixin needs to be included in the admin that hosts the ``inlines``.
It makes sure the generated admin forms have different fieldsets/fields
depending on the polymorphic type of the form instance.
This is achieved by overwriting :func:`get_inline_formsets` to return
an :class:`InlinePolymorphicAdminFormSet` instead of a standard Django
:class:`~django.contrib.admin.helpers.InlineAdminFormSet` for the polymorphic formsets.
""" """
def get_inline_formsets(self, request, formsets, inline_instances, obj=None): def get_inline_formsets(self, request, formsets, inline_instances, obj=None):
@ -88,7 +96,8 @@ class PolymorphicInlineSupportMixin(object):
for admin_formset in inline_admin_formsets: for admin_formset in inline_admin_formsets:
if isinstance(admin_formset.formset, BasePolymorphicModelFormSet): if isinstance(admin_formset.formset, BasePolymorphicModelFormSet):
# Downcast the admin # This is a polymorphic formset, which belongs to our inline.
# Downcast the admin wrapper that generates the form fields.
admin_formset.__class__ = InlinePolymorphicAdminFormSet admin_formset.__class__ = InlinePolymorphicAdminFormSet
admin_formset.request = request admin_formset.request = request
admin_formset.obj = obj admin_formset.obj = obj

View File

@ -17,11 +17,10 @@ class PolymorphicParentInlineModelAdmin(InlineModelAdmin):
""" """
A polymorphic inline, where each formset row can be a different form. A polymorphic inline, where each formset row can be a different form.
Note that Note that:
* Permissions are only checked on the base model. * Permissions are only checked on the base model.
* The child inlines can't override the base model fields, only this parent inline can do that. * The child inlines can't override the base model fields, only this parent inline can do that.
* Child formset media is not yet processed.
""" """
formset = BasePolymorphicInlineFormSet formset = BasePolymorphicInlineFormSet
@ -79,7 +78,7 @@ class PolymorphicParentInlineModelAdmin(InlineModelAdmin):
# Instead of completely redefining super().get_formset(), we use # Instead of completely redefining super().get_formset(), we use
# the regular inlineformset_factory(), and amend that with our extra bits. # the regular inlineformset_factory(), and amend that with our extra bits.
# This is identical to what polymorphic_inlineformset_factory() does. # This code line is the essence of what polymorphic_inlineformset_factory() does.
FormSet.child_forms = polymorphic_child_forms_factory( FormSet.child_forms = polymorphic_child_forms_factory(
formset_children=self.get_formset_children(request, obj=obj) formset_children=self.get_formset_children(request, obj=obj)
) )

View File

@ -14,6 +14,7 @@ class PolymorphicFormSetChild(object):
Metadata to define the inline of a polymorphic child. Metadata to define the inline of a polymorphic child.
Provide this information in the :func:`polymorphic_inlineformset_factory` construction. Provide this information in the :func:`polymorphic_inlineformset_factory` construction.
""" """
def __init__(self, model, form=ModelForm, fields=None, exclude=None, def __init__(self, model, form=ModelForm, fields=None, exclude=None,
formfield_callback=None, widgets=None, localized_fields=None, formfield_callback=None, widgets=None, localized_fields=None,
labels=None, help_texts=None, error_messages=None): labels=None, help_texts=None, error_messages=None):
@ -77,6 +78,9 @@ class PolymorphicFormSetChild(object):
def polymorphic_child_forms_factory(formset_children, **kwargs): def polymorphic_child_forms_factory(formset_children, **kwargs):
""" """
Construct the forms for the formset children. Construct the forms for the formset children.
This is mostly used internally, and rarely needs to be used by external projects.
When using the factory methods (:func:`polymorphic_inlineformset_factory`),
this feature is called already for you.
""" """
child_forms = OrderedDict() child_forms = OrderedDict()
@ -97,6 +101,7 @@ class BasePolymorphicModelFormSet(BaseModelFormSet):
note that the ID field will no longer be named ``model_ptr``, note that the ID field will no longer be named ``model_ptr``,
but just appear as ``id``. but just appear as ``id``.
""" """
# Assigned by the factory # Assigned by the factory
child_forms = OrderedDict() child_forms = OrderedDict()
@ -191,6 +196,9 @@ class BasePolymorphicModelFormSet(BaseModelFormSet):
super(BasePolymorphicModelFormSet, self).add_fields(form, index) super(BasePolymorphicModelFormSet, self).add_fields(form, index)
def get_form_class(self, model): def get_form_class(self, model):
"""
Return the proper form class for the given model.
"""
if not self.child_forms: if not self.child_forms:
raise ImproperlyConfigured("No 'child_forms' defined in {0}".format(self.__class__.__name__)) raise ImproperlyConfigured("No 'child_forms' defined in {0}".format(self.__class__.__name__))
return self.child_forms[model] return self.child_forms[model]