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.
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:

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
admin
formsets
performance
Advanced topics

View File

@ -6,8 +6,10 @@ from django.core.urlresolvers import resolve
from django.utils import six
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.

View File

@ -7,6 +7,12 @@ class PolymorphicChildModelFilter(admin.SimpleListFilter):
"""
An admin list filter for the PolymorphicParentModelAdmin which enables
filtering by its child models.
This can be used in the parent admin:
.. code-block:: python
list_filter = (PolymorphicChildModelFilter,)
"""
title = _('Type')
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 ..formsets import BasePolymorphicModelFormSet
from polymorphic.formsets import BasePolymorphicModelFormSet
class InlinePolymorphicAdminForm(InlineAdminForm):
@ -75,6 +75,14 @@ class InlinePolymorphicAdminFormSet(InlineAdminFormSet):
class PolymorphicInlineSupportMixin(object):
"""
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):
@ -88,7 +96,8 @@ class PolymorphicInlineSupportMixin(object):
for admin_formset in inline_admin_formsets:
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.request = request
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.
Note that
Note that:
* 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.
* Child formset media is not yet processed.
"""
formset = BasePolymorphicInlineFormSet
@ -79,7 +78,7 @@ class PolymorphicParentInlineModelAdmin(InlineModelAdmin):
# Instead of completely redefining super().get_formset(), we use
# 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_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.
Provide this information in the :func:`polymorphic_inlineformset_factory` construction.
"""
def __init__(self, model, form=ModelForm, fields=None, exclude=None,
formfield_callback=None, widgets=None, localized_fields=None,
labels=None, help_texts=None, error_messages=None):
@ -77,6 +78,9 @@ class PolymorphicFormSetChild(object):
def polymorphic_child_forms_factory(formset_children, **kwargs):
"""
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()
@ -97,6 +101,7 @@ class BasePolymorphicModelFormSet(BaseModelFormSet):
note that the ID field will no longer be named ``model_ptr``,
but just appear as ``id``.
"""
# Assigned by the factory
child_forms = OrderedDict()
@ -191,6 +196,9 @@ class BasePolymorphicModelFormSet(BaseModelFormSet):
super(BasePolymorphicModelFormSet, self).add_fields(form, index)
def get_form_class(self, model):
"""
Return the proper form class for the given model.
"""
if not self.child_forms:
raise ImproperlyConfigured("No 'child_forms' defined in {0}".format(self.__class__.__name__))
return self.child_forms[model]