Rename inline/formset classes to avoid more confusion.

AbstractSingletonProxyFactoryBean danger luked here...
Added the Stacked...Inline for clarity too.
fix_request_path_info
Diederik van der Boor 2016-08-09 01:11:05 +02:00
parent 8c42893abd
commit 1f0ddd8436
6 changed files with 157 additions and 143 deletions

View File

@ -14,22 +14,22 @@ from .filters import PolymorphicChildModelFilter
# Inlines
from .inlines import (
PolymorphicParentInlineModelAdmin,
PolymorphicChildInlineModelAdmin,
PolymorphicInlineModelAdmin, # base class
StackedPolymorphicInline, # stacked inline
)
# Helpers for the inlines
from .helpers import (
InlinePolymorphicAdminForm,
InlinePolymorphicAdminFormSet,
PolymorphicInlineAdminForm,
PolymorphicInlineAdminFormSet,
PolymorphicInlineSupportMixin, # mixin for the regular model admin!
)
# Expose generic admin features too. There is no need to split those
# as the admin already relies on contenttypes.
from .generic import (
PolymorphicParentGenericInlineModelAdmin,
PolymorphicChildGenericInlineModelAdmin,
GenericPolymorphicInlineModelAdmin, # base class
GenericStackedPolymorphicInline, # stacked inline
)
__all__ = (
@ -37,11 +37,10 @@ __all__ = (
'PolymorphicChildModelAdmin',
'PolymorphicModelChoiceForm',
'PolymorphicChildModelFilter',
'InlinePolymorphicAdminForm',
'InlinePolymorphicAdminFormSet',
'PolymorphicInlineAdminForm',
'PolymorphicInlineAdminFormSet',
'PolymorphicInlineSupportMixin',
'PolymorphicParentInlineModelAdmin',
'PolymorphicChildInlineModelAdmin',
'PolymorphicParentGenericInlineModelAdmin',
'PolymorphicChildGenericInlineModelAdmin',
'PolymorphicInlineModelAdmin',
'GenericPolymorphicInlineModelAdmin',
'GenericStackedPolymorphicInline',
)

View File

@ -2,15 +2,15 @@ from django.contrib.contenttypes.admin import GenericInlineModelAdmin
from django.contrib.contenttypes.models import ContentType
from django.utils.functional import cached_property
from polymorphic.formsets import polymorphic_child_forms_factory, BasePolymorphicGenericInlineFormSet, PolymorphicGenericFormSetChild
from .inlines import PolymorphicParentInlineModelAdmin, PolymorphicChildInlineModelAdmin
from polymorphic.formsets import polymorphic_child_forms_factory, BaseGenericPolymorphicInlineFormSet, GenericPolymorphicFormSetChild
from .inlines import PolymorphicInlineModelAdmin
class PolymorphicParentGenericInlineModelAdmin(PolymorphicParentInlineModelAdmin, GenericInlineModelAdmin):
class GenericPolymorphicInlineModelAdmin(PolymorphicInlineModelAdmin, GenericInlineModelAdmin):
"""
Variation for inlines based on generic foreign keys.
Base class for variation of inlines based on generic foreign keys.
"""
formset = BasePolymorphicGenericInlineFormSet
formset = BaseGenericPolymorphicInlineFormSet
def get_formset(self, request, obj=None, **kwargs):
"""
@ -26,30 +26,36 @@ class PolymorphicParentGenericInlineModelAdmin(PolymorphicParentInlineModelAdmin
)
return FormSet
class PolymorphicChildGenericInlineModelAdmin(PolymorphicChildInlineModelAdmin):
"""
Variation for generic inlines.
"""
# Make sure that the GFK fields are excluded from the child forms
formset_child = PolymorphicGenericFormSetChild
ct_field = "content_type"
ct_fk_field = "object_id"
@cached_property
def content_type(self):
class Child(PolymorphicInlineModelAdmin.Child):
"""
Expose the ContentType that the child relates to.
This can be used for the ``polymorphic_ctype`` field.
Variation for generic inlines.
"""
return ContentType.objects.get_for_model(self.model)
# Make sure that the GFK fields are excluded from the child forms
formset_child = GenericPolymorphicFormSetChild
ct_field = "content_type"
ct_fk_field = "object_id"
def get_formset_child(self, request, obj=None, **kwargs):
# Similar to GenericInlineModelAdmin.get_formset(),
# make sure the GFK is automatically excluded from the form
defaults = {
"ct_field": self.ct_field,
"fk_field": self.ct_fk_field,
}
defaults.update(kwargs)
return super(PolymorphicChildGenericInlineModelAdmin, self).get_formset_child(request, obj=obj, **defaults)
@cached_property
def content_type(self):
"""
Expose the ContentType that the child relates to.
This can be used for the ``polymorphic_ctype`` field.
"""
return ContentType.objects.get_for_model(self.model)
def get_formset_child(self, request, obj=None, **kwargs):
# Similar to GenericInlineModelAdmin.get_formset(),
# make sure the GFK is automatically excluded from the form
defaults = {
"ct_field": self.ct_field,
"fk_field": self.ct_fk_field,
}
defaults.update(kwargs)
return super(GenericPolymorphicInlineModelAdmin.Child, self).get_formset_child(request, obj=obj, **defaults)
class GenericStackedPolymorphicInline(GenericPolymorphicInlineModelAdmin):
"""
The stacked layout for generic inlines.
"""
template = 'admin/polymorphic/edit_inline/stacked.html'

View File

@ -3,19 +3,19 @@ Rendering utils for admin forms;
This makes sure that admin fieldsets/layout settings are exported to the template.
"""
from django.contrib.admin.helpers import InlineAdminFormSet, InlineAdminForm
import django
from django.contrib.admin.helpers import InlineAdminFormSet, InlineAdminForm, AdminField
from polymorphic.formsets import BasePolymorphicModelFormSet
class InlinePolymorphicAdminForm(InlineAdminForm):
class PolymorphicInlineAdminForm(InlineAdminForm):
"""
Expose the admin configuration for a form
"""
pass
class InlinePolymorphicAdminFormSet(InlineAdminFormSet):
class PolymorphicInlineAdminFormSet(InlineAdminFormSet):
"""
Internally used class to expose the formset in the template.
"""
@ -23,7 +23,7 @@ class InlinePolymorphicAdminFormSet(InlineAdminFormSet):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None) # Assigned later via PolymorphicInlineSupportMixin later.
self.obj = kwargs.pop('obj', None)
super(InlinePolymorphicAdminFormSet, self).__init__(*args, **kwargs)
super(PolymorphicInlineAdminFormSet, self).__init__(*args, **kwargs)
def __iter__(self):
"""
@ -35,7 +35,7 @@ class InlinePolymorphicAdminFormSet(InlineAdminFormSet):
child_inline = self.opts.get_child_inline_instance(model)
view_on_site_url = self.opts.get_view_on_site_url(original)
yield InlinePolymorphicAdminForm(
yield PolymorphicInlineAdminForm(
formset=self.formset,
form=form,
fieldsets=self.get_child_fieldsets(child_inline),
@ -50,7 +50,7 @@ class InlinePolymorphicAdminFormSet(InlineAdminFormSet):
for form in self.formset.extra_forms + self.formset.empty_forms:
model = form._meta.model
child_inline = self.opts.get_child_inline_instance(model)
yield InlinePolymorphicAdminForm(
yield PolymorphicInlineAdminForm(
formset=self.formset,
form=form,
fieldsets=self.get_child_fieldsets(child_inline),
@ -81,7 +81,7 @@ class PolymorphicInlineSupportMixin(object):
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
an :class:`PolymorphicInlineAdminFormSet` instead of a standard Django
:class:`~django.contrib.admin.helpers.InlineAdminFormSet` for the polymorphic formsets.
"""
@ -98,7 +98,7 @@ class PolymorphicInlineSupportMixin(object):
if isinstance(admin_formset.formset, BasePolymorphicModelFormSet):
# 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__ = PolymorphicInlineAdminFormSet
admin_formset.request = request
admin_formset.obj = obj
return inline_admin_formsets

View File

@ -13,7 +13,7 @@ from polymorphic.formsets import polymorphic_child_forms_factory, BasePolymorphi
from polymorphic.formsets.utils import add_media
class PolymorphicParentInlineModelAdmin(InlineModelAdmin):
class PolymorphicInlineModelAdmin(InlineModelAdmin):
"""
A polymorphic inline, where each formset row can be a different form.
@ -30,11 +30,11 @@ class PolymorphicParentInlineModelAdmin(InlineModelAdmin):
extra = 0
#: Inlines for all model sub types that can be displayed in this inline.
#: Each row is a :class:`PolymorphicChildInlineModelAdmin`
#: Each row is a :class:`PolymorphicInlineModelAdmin.Child`
child_inlines = ()
def __init__(self, parent_model, admin_site):
super(PolymorphicParentInlineModelAdmin, self).__init__(parent_model, admin_site)
super(PolymorphicInlineModelAdmin, self).__init__(parent_model, admin_site)
# While the inline is created per request, the 'request' object is not known here.
# Hence, creating all child inlines unconditionally, without checking permissions.
@ -47,7 +47,7 @@ class PolymorphicParentInlineModelAdmin(InlineModelAdmin):
def get_child_inline_instances(self):
"""
:rtype List[PolymorphicChildInlineModelAdmin]
:rtype List[PolymorphicInlineModelAdmin.Child]
"""
instances = []
for ChildInlineType in self.child_inlines:
@ -58,7 +58,7 @@ class PolymorphicParentInlineModelAdmin(InlineModelAdmin):
"""
Find the child inline for a given model.
:rtype: PolymorphicChildInlineModelAdmin
:rtype: PolymorphicInlineModelAdmin.Child
"""
try:
return self._child_inlines_lookup[model]
@ -74,7 +74,7 @@ class PolymorphicParentInlineModelAdmin(InlineModelAdmin):
:rtype: type
"""
# Construct the FormSet class
FormSet = super(PolymorphicParentInlineModelAdmin, self).get_formset(request, obj=obj, **kwargs)
FormSet = super(PolymorphicInlineModelAdmin, self).get_formset(request, obj=obj, **kwargs)
# Instead of completely redefining super().get_formset(), we use
# the regular inlineformset_factory(), and amend that with our extra bits.
@ -115,7 +115,7 @@ class PolymorphicParentInlineModelAdmin(InlineModelAdmin):
# The media of the inline focuses on the admin settings,
# whether to expose the scripts for filter_horizontal etc..
# The admin helper exposes the inline + formset media.
base_media = super(PolymorphicParentInlineModelAdmin, self).media
base_media = super(PolymorphicInlineModelAdmin, self).media
all_media = Media()
add_media(all_media, base_media)
@ -127,87 +127,96 @@ class PolymorphicParentInlineModelAdmin(InlineModelAdmin):
if child_media._css != base_media._css and child_media._js != base_media._js:
add_media(all_media, child_media)
add_media(all_media, self.polymorphic_media)
return all_media
class PolymorphicChildInlineModelAdmin(InlineModelAdmin):
"""
The child inline; which allows configuring the admin options
for the child appearance.
Note that not all options will be honored by the parent, notably the formset options:
* :attr:`extra`
* :attr:`min_num`
* :attr:`max_num`
The model form options however, will all be read.
"""
formset_child = PolymorphicFormSetChild
extra = 0 # TODO: currently unused for the children.
def __init__(self, parent_inline):
self.parent_inline = parent_inline
super(PolymorphicChildInlineModelAdmin, self).__init__(parent_inline.parent_model, parent_inline.admin_site)
def get_formset(self, request, obj=None, **kwargs):
# The child inline is only used to construct the form,
# and allow to override the form field attributes.
# The formset is created by the parent inline.
raise RuntimeError("The child get_formset() is not used.")
def get_fields(self, request, obj=None):
if self.fields:
return self.fields
# Standard Django logic, use the form to determine the fields.
# The form needs to pass through all factory logic so all 'excludes' are set as well.
# Default Django does: form = self.get_formset(request, obj, fields=None).form
# Use 'fields=None' avoids recursion in the field autodetection.
form = self.get_formset_child(request, obj, fields=None).get_form()
return list(form.base_fields) + list(self.get_readonly_fields(request, obj))
def get_formset_child(self, request, obj=None, **kwargs):
class Child(InlineModelAdmin):
"""
Return the formset child that the parent inline can use to represent us.
The child inline; which allows configuring the admin options
for the child appearance.
:rtype: PolymorphicFormSetChild
Note that not all options will be honored by the parent, notably the formset options:
* :attr:`extra`
* :attr:`min_num`
* :attr:`max_num`
The model form options however, will all be read.
"""
# Similar to the normal get_formset(), the caller may pass fields to override the defaults settings
# in the inline. In Django's GenericInlineModelAdmin.get_formset() this is also used in the same way,
# to make sure the 'exclude' also contains the GFK fields.
#
# Hence this code is almost identical to InlineModelAdmin.get_formset()
# and GenericInlineModelAdmin.get_formset()
#
# Transfer the local inline attributes to the formset child,
# this allows overriding settings.
if 'fields' in kwargs:
fields = kwargs.pop('fields')
else:
fields = flatten_fieldsets(self.get_fieldsets(request, obj))
formset_child = PolymorphicFormSetChild
extra = 0 # TODO: currently unused for the children.
if self.exclude is None:
exclude = []
else:
exclude = list(self.exclude)
def __init__(self, parent_inline):
self.parent_inline = parent_inline
super(PolymorphicInlineModelAdmin.Child, self).__init__(parent_inline.parent_model, parent_inline.admin_site)
exclude.extend(self.get_readonly_fields(request, obj))
def get_formset(self, request, obj=None, **kwargs):
# The child inline is only used to construct the form,
# and allow to override the form field attributes.
# The formset is created by the parent inline.
raise RuntimeError("The child get_formset() is not used.")
if self.exclude is None and hasattr(self.form, '_meta') and self.form._meta.exclude:
# Take the custom ModelForm's Meta.exclude into account only if the
# InlineModelAdmin doesn't define its own.
exclude.extend(self.form._meta.exclude)
def get_fields(self, request, obj=None):
if self.fields:
return self.fields
#can_delete = self.can_delete and self.has_delete_permission(request, obj)
defaults = {
"form": self.form,
"fields": fields,
"exclude": exclude or None,
"formfield_callback": partial(self.formfield_for_dbfield, request=request),
}
defaults.update(kwargs)
# Standard Django logic, use the form to determine the fields.
# The form needs to pass through all factory logic so all 'excludes' are set as well.
# Default Django does: form = self.get_formset(request, obj, fields=None).form
# Use 'fields=None' avoids recursion in the field autodetection.
form = self.get_formset_child(request, obj, fields=None).get_form()
return list(form.base_fields) + list(self.get_readonly_fields(request, obj))
# This goes through the same logic that get_formset() calls
# by passing the inline class attributes to modelform_factory()
FormSetChildClass = self.formset_child
return FormSetChildClass(self.model, **defaults)
def get_formset_child(self, request, obj=None, **kwargs):
"""
Return the formset child that the parent inline can use to represent us.
:rtype: PolymorphicFormSetChild
"""
# Similar to the normal get_formset(), the caller may pass fields to override the defaults settings
# in the inline. In Django's GenericInlineModelAdmin.get_formset() this is also used in the same way,
# to make sure the 'exclude' also contains the GFK fields.
#
# Hence this code is almost identical to InlineModelAdmin.get_formset()
# and GenericInlineModelAdmin.get_formset()
#
# Transfer the local inline attributes to the formset child,
# this allows overriding settings.
if 'fields' in kwargs:
fields = kwargs.pop('fields')
else:
fields = flatten_fieldsets(self.get_fieldsets(request, obj))
if self.exclude is None:
exclude = []
else:
exclude = list(self.exclude)
exclude.extend(self.get_readonly_fields(request, obj))
if self.exclude is None and hasattr(self.form, '_meta') and self.form._meta.exclude:
# Take the custom ModelForm's Meta.exclude into account only if the
# InlineModelAdmin doesn't define its own.
exclude.extend(self.form._meta.exclude)
#can_delete = self.can_delete and self.has_delete_permission(request, obj)
defaults = {
"form": self.form,
"fields": fields,
"exclude": exclude or None,
"formfield_callback": partial(self.formfield_for_dbfield, request=request),
}
defaults.update(kwargs)
# This goes through the same logic that get_formset() calls
# by passing the inline class attributes to modelform_factory()
FormSetChildClass = self.formset_child
return FormSetChildClass(self.model, **defaults)
class StackedPolymorphicInline(PolymorphicInlineModelAdmin):
"""
Stacked inline for django-polymorphic models.
Since tabular doesn't make much sense with changed fields, just offer this one.
"""
template = 'admin/polymorphic/edit_inline/stacked.html'

View File

@ -34,18 +34,18 @@ from .models import (
)
from .generic import (
# Can import generic here, as polymorphic already depends on the 'contenttypes' app.
BasePolymorphicGenericInlineFormSet,
PolymorphicGenericFormSetChild,
polymorphic_generic_inlineformset_factory,
BaseGenericPolymorphicInlineFormSet,
GenericPolymorphicFormSetChild,
generic_polymorphic_inlineformset_factory,
)
__all__ = (
'BasePolymorphicModelFormSet',
'BasePolymorphicInlineFormSet',
'BasePolymorphicGenericInlineFormSet',
'PolymorphicFormSetChild',
'PolymorphicGenericFormSetChild',
'polymorphic_inlineformset_factory',
'polymorphic_generic_inlineformset_factory',
'polymorphic_child_forms_factory',
'BaseGenericPolymorphicInlineFormSet',
'GenericPolymorphicFormSetChild',
'generic_polymorphic_inlineformset_factory',
)

View File

@ -7,14 +7,14 @@ from django.forms.models import ModelForm
from .models import BasePolymorphicModelFormSet, polymorphic_child_forms_factory, PolymorphicFormSetChild
class PolymorphicGenericFormSetChild(PolymorphicFormSetChild):
class GenericPolymorphicFormSetChild(PolymorphicFormSetChild):
"""
Formset child for generic inlines
"""
def __init__(self, *args, **kwargs):
self.ct_field = kwargs.pop('ct_field', 'content_type')
self.fk_field = kwargs.pop('fk_field', 'object_id')
super(PolymorphicGenericFormSetChild, self).__init__(*args, **kwargs)
super(GenericPolymorphicFormSetChild, self).__init__(*args, **kwargs)
def get_form(self, ct_field="content_type", fk_field="object_id", **kwargs):
"""
@ -42,17 +42,17 @@ class PolymorphicGenericFormSetChild(PolymorphicFormSetChild):
exclude.extend([ct_field.name, fk_field.name])
kwargs['exclude'] = exclude
return super(PolymorphicGenericFormSetChild, self).get_form(**kwargs)
return super(GenericPolymorphicFormSetChild, self).get_form(**kwargs)
class BasePolymorphicGenericInlineFormSet(BaseGenericInlineFormSet, BasePolymorphicModelFormSet):
class BaseGenericPolymorphicInlineFormSet(BaseGenericInlineFormSet, BasePolymorphicModelFormSet):
"""
Polymorphic formset variation for inline generic formsets
"""
def polymorphic_generic_inlineformset_factory(model, formset_children, form=ModelForm,
formset=BasePolymorphicGenericInlineFormSet,
def generic_polymorphic_inlineformset_factory(model, formset_children, form=ModelForm,
formset=BaseGenericPolymorphicInlineFormSet,
ct_field="content_type", fk_field="object_id",
# Base form
# TODO: should these fields be removed in favor of creating