Reformat all files with isort and black

This commit is contained in:
Diederik van der Boor
2019-07-15 09:50:03 +02:00
parent d314dce4a2
commit 59c020ee50
50 changed files with 4069 additions and 2419 deletions
+25 -31
View File
@@ -4,44 +4,38 @@ ModelAdmin code to display polymorphic models.
The admin consists of a parent admin (which shows in the admin with a list),
and a child admin (which is used internally to show the edit/delete dialog).
"""
# Admins for the regular models
from .parentadmin import PolymorphicParentModelAdmin
from .childadmin import PolymorphicChildModelAdmin
from .filters import PolymorphicChildModelFilter
# Utils
from .forms import PolymorphicModelChoiceForm
from .filters import PolymorphicChildModelFilter
# Inlines
from .inlines import (
PolymorphicInlineModelAdmin, # base class
StackedPolymorphicInline, # stacked inline
)
# Helpers for the inlines
from .helpers import (
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 (
GenericPolymorphicInlineModelAdmin, # base class
GenericStackedPolymorphicInline, # stacked inline
)
from .generic import GenericPolymorphicInlineModelAdmin # base class
from .generic import GenericStackedPolymorphicInline # stacked inline
# Helpers for the inlines
from .helpers import PolymorphicInlineSupportMixin # mixin for the regular model admin!
from .helpers import PolymorphicInlineAdminForm, PolymorphicInlineAdminFormSet
# Inlines
from .inlines import PolymorphicInlineModelAdmin # base class
from .inlines import StackedPolymorphicInline # stacked inline
# Admins for the regular models
from .parentadmin import PolymorphicParentModelAdmin
__all__ = (
'PolymorphicParentModelAdmin',
'PolymorphicChildModelAdmin',
'PolymorphicModelChoiceForm',
'PolymorphicChildModelFilter',
'PolymorphicInlineAdminForm',
'PolymorphicInlineAdminFormSet',
'PolymorphicInlineSupportMixin',
'PolymorphicInlineModelAdmin',
'StackedPolymorphicInline',
'GenericPolymorphicInlineModelAdmin',
'GenericStackedPolymorphicInline',
"PolymorphicParentModelAdmin",
"PolymorphicChildModelAdmin",
"PolymorphicModelChoiceForm",
"PolymorphicChildModelFilter",
"PolymorphicInlineAdminForm",
"PolymorphicInlineAdminFormSet",
"PolymorphicInlineSupportMixin",
"PolymorphicInlineModelAdmin",
"StackedPolymorphicInline",
"GenericPolymorphicInlineModelAdmin",
"GenericStackedPolymorphicInline",
)
+51 -32
View File
@@ -8,6 +8,7 @@ from django.urls import resolve
from django.utils.translation import ugettext_lazy as _
from polymorphic.utils import get_base_polymorphic_model
from ..admin import PolymorphicParentModelAdmin
@@ -46,7 +47,9 @@ class PolymorphicChildModelAdmin(admin.ModelAdmin):
show_in_index = False
def __init__(self, model, admin_site, *args, **kwargs):
super(PolymorphicChildModelAdmin, self).__init__(model, admin_site, *args, **kwargs)
super(PolymorphicChildModelAdmin, self).__init__(
model, admin_site, *args, **kwargs
)
if self.base_model is None:
self.base_model = get_base_polymorphic_model(model)
@@ -59,19 +62,23 @@ class PolymorphicChildModelAdmin(admin.ModelAdmin):
#
# Instead, pass the form unchecked here, because the standard ModelForm will just work.
# If the derived class sets the model explicitly, respect that setting.
kwargs.setdefault('form', self.base_form or self.form)
kwargs.setdefault("form", self.base_form or self.form)
# prevent infinite recursion when this is called from get_subclass_fields
if not self.fieldsets and not self.fields:
kwargs.setdefault('fields', '__all__')
kwargs.setdefault("fields", "__all__")
return super(PolymorphicChildModelAdmin, self).get_form(request, obj, **kwargs)
def get_model_perms(self, request):
match = resolve(request.path)
if not self.show_in_index and match.app_name == 'admin' and match.url_name in ('index', 'app_list'):
return {'add': False, 'change': False, 'delete': False}
if (
not self.show_in_index
and match.app_name == "admin"
and match.url_name in ("index", "app_list")
):
return {"add": False, "change": False, "delete": False}
return super(PolymorphicChildModelAdmin, self).get_model_perms(request)
@property
@@ -87,10 +94,11 @@ class PolymorphicChildModelAdmin(admin.ModelAdmin):
"admin/%s/%s/change_form.html" % (app_label, opts.object_name.lower()),
"admin/%s/change_form.html" % app_label,
# Added:
"admin/%s/%s/change_form.html" % (base_app_label, base_opts.object_name.lower()),
"admin/%s/%s/change_form.html"
% (base_app_label, base_opts.object_name.lower()),
"admin/%s/change_form.html" % base_app_label,
"admin/polymorphic/change_form.html",
"admin/change_form.html"
"admin/change_form.html",
]
@property
@@ -103,13 +111,15 @@ class PolymorphicChildModelAdmin(admin.ModelAdmin):
base_app_label = base_opts.app_label
return [
"admin/%s/%s/delete_confirmation.html" % (app_label, opts.object_name.lower()),
"admin/%s/%s/delete_confirmation.html"
% (app_label, opts.object_name.lower()),
"admin/%s/delete_confirmation.html" % app_label,
# Added:
"admin/%s/%s/delete_confirmation.html" % (base_app_label, base_opts.object_name.lower()),
"admin/%s/%s/delete_confirmation.html"
% (base_app_label, base_opts.object_name.lower()),
"admin/%s/delete_confirmation.html" % base_app_label,
"admin/polymorphic/delete_confirmation.html",
"admin/delete_confirmation.html"
"admin/delete_confirmation.html",
]
@property
@@ -125,15 +135,16 @@ class PolymorphicChildModelAdmin(admin.ModelAdmin):
"admin/%s/%s/object_history.html" % (app_label, opts.object_name.lower()),
"admin/%s/object_history.html" % app_label,
# Added:
"admin/%s/%s/object_history.html" % (base_app_label, base_opts.object_name.lower()),
"admin/%s/%s/object_history.html"
% (base_app_label, base_opts.object_name.lower()),
"admin/%s/object_history.html" % base_app_label,
"admin/polymorphic/object_history.html",
"admin/object_history.html"
"admin/object_history.html",
]
def _get_parent_admin(self):
# this returns parent admin instance on which to call response_post_save methods
parent_model = self.model._meta.get_field('polymorphic_ctype').model
parent_model = self.model._meta.get_field("polymorphic_ctype").model
if parent_model == self.model:
# when parent_model is in among child_models, just return super instance
return super(PolymorphicChildModelAdmin, self)
@@ -149,11 +160,15 @@ class PolymorphicChildModelAdmin(admin.ModelAdmin):
# Fetch admin instance for model class, see if it's a possible candidate.
model_admin = self.admin_site._registry.get(klass)
if model_admin is not None and isinstance(model_admin, PolymorphicParentModelAdmin):
if model_admin is not None and isinstance(
model_admin, PolymorphicParentModelAdmin
):
return model_admin # Success!
# If we get this far without returning there is no admin available
raise ParentAdminNotRegistered("No parent admin was registered for a '{0}' model.".format(parent_model))
raise ParentAdminNotRegistered(
"No parent admin was registered for a '{0}' model.".format(parent_model)
)
def response_post_save_add(self, request, obj):
return self._get_parent_admin().response_post_save_add(request, obj)
@@ -161,26 +176,28 @@ class PolymorphicChildModelAdmin(admin.ModelAdmin):
def response_post_save_change(self, request, obj):
return self._get_parent_admin().response_post_save_change(request, obj)
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
context.update({
'base_opts': self.base_model._meta,
})
return super(PolymorphicChildModelAdmin, self).render_change_form(request, context, add=add, change=change, form_url=form_url, obj=obj)
def render_change_form(
self, request, context, add=False, change=False, form_url="", obj=None
):
context.update({"base_opts": self.base_model._meta})
return super(PolymorphicChildModelAdmin, self).render_change_form(
request, context, add=add, change=change, form_url=form_url, obj=obj
)
def delete_view(self, request, object_id, context=None):
extra_context = {
'base_opts': self.base_model._meta,
}
return super(PolymorphicChildModelAdmin, self).delete_view(request, object_id, extra_context)
extra_context = {"base_opts": self.base_model._meta}
return super(PolymorphicChildModelAdmin, self).delete_view(
request, object_id, extra_context
)
def history_view(self, request, object_id, extra_context=None):
# Make sure the history view can also display polymorphic breadcrumbs
context = {
'base_opts': self.base_model._meta,
}
context = {"base_opts": self.base_model._meta}
if extra_context:
context.update(extra_context)
return super(PolymorphicChildModelAdmin, self).history_view(request, object_id, extra_context=context)
return super(PolymorphicChildModelAdmin, self).history_view(
request, object_id, extra_context=context
)
# ---- Extra: improving the form/fieldset default display ----
@@ -201,7 +218,7 @@ class PolymorphicChildModelAdmin(admin.ModelAdmin):
if other_fields:
return (
base_fieldsets[0],
(self.extra_fieldset_title, {'fields': other_fields}),
(self.extra_fieldset_title, {"fields": other_fields}),
) + base_fieldsets[1:]
else:
return base_fieldsets
@@ -215,13 +232,15 @@ class PolymorphicChildModelAdmin(admin.ModelAdmin):
# By not declaring the fields/form in the base class,
# get_form() will populate the form with all available fields.
form = self.get_form(request, obj, exclude=exclude)
subclass_fields = list(form.base_fields.keys()) + list(self.get_readonly_fields(request, obj))
subclass_fields = list(form.base_fields.keys()) + list(
self.get_readonly_fields(request, obj)
)
# Find which fields are not part of the common fields.
for fieldset in self.get_base_fieldsets(request, obj):
for field in fieldset[1]['fields']:
for field in fieldset[1]["fields"]:
try:
subclass_fields.remove(field)
except ValueError:
pass # field not found in form, Django will raise exception later.
pass # field not found in form, Django will raise exception later.
return subclass_fields
+8 -4
View File
@@ -14,11 +14,12 @@ class PolymorphicChildModelFilter(admin.SimpleListFilter):
list_filter = (PolymorphicChildModelFilter,)
"""
title = _('Type')
parameter_name = 'polymorphic_ctype'
title = _("Type")
parameter_name = "polymorphic_ctype"
def lookups(self, request, model_admin):
return model_admin.get_child_type_choices(request, 'change')
return model_admin.get_child_type_choices(request, "change")
def queryset(self, request, queryset):
try:
@@ -31,5 +32,8 @@ class PolymorphicChildModelFilter(admin.SimpleListFilter):
if choice_value == value:
return queryset.filter(polymorphic_ctype_id=choice_value)
raise PermissionDenied(
'Invalid ContentType "{0}". It must be registered as child model.'.format(value))
'Invalid ContentType "{0}". It must be registered as child model.'.format(
value
)
)
return queryset
+7 -4
View File
@@ -7,12 +7,15 @@ class PolymorphicModelChoiceForm(forms.Form):
"""
The default form for the ``add_type_form``. Can be overwritten and replaced.
"""
#: Define the label for the radiofield
type_label = _('Type')
ct_id = forms.ChoiceField(label=type_label, widget=AdminRadioSelect(attrs={'class': 'radiolist'}))
#: Define the label for the radiofield
type_label = _("Type")
ct_id = forms.ChoiceField(
label=type_label, widget=AdminRadioSelect(attrs={"class": "radiolist"})
)
def __init__(self, *args, **kwargs):
# Allow to easily redefine the label (a commonly expected usecase)
super(PolymorphicModelChoiceForm, self).__init__(*args, **kwargs)
self.fields['ct_id'].label = self.type_label
self.fields["ct_id"].label = self.type_label
+20 -9
View File
@@ -2,14 +2,22 @@ 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, BaseGenericPolymorphicInlineFormSet, GenericPolymorphicFormSetChild
from polymorphic.formsets import (
BaseGenericPolymorphicInlineFormSet,
GenericPolymorphicFormSetChild,
polymorphic_child_forms_factory,
)
from .inlines import PolymorphicInlineModelAdmin
class GenericPolymorphicInlineModelAdmin(PolymorphicInlineModelAdmin, GenericInlineModelAdmin):
class GenericPolymorphicInlineModelAdmin(
PolymorphicInlineModelAdmin, GenericInlineModelAdmin
):
"""
Base class for variation of inlines based on generic foreign keys.
"""
#: The formset class
formset = BaseGenericPolymorphicInlineFormSet
@@ -31,6 +39,7 @@ class GenericPolymorphicInlineModelAdmin(PolymorphicInlineModelAdmin, GenericInl
"""
Variation for generic inlines.
"""
# Make sure that the GFK fields are excluded from the child forms
formset_child = GenericPolymorphicFormSetChild
ct_field = "content_type"
@@ -42,22 +51,24 @@ class GenericPolymorphicInlineModelAdmin(PolymorphicInlineModelAdmin, GenericInl
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, for_concrete_model=False)
return ContentType.objects.get_for_model(
self.model, for_concrete_model=False
)
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 = {"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)
return super(
GenericPolymorphicInlineModelAdmin.Child, self
).get_formset_child(request, obj=obj, **defaults)
class GenericStackedPolymorphicInline(GenericPolymorphicInlineModelAdmin):
"""
The stacked layout for generic inlines.
"""
#: The default template to use.
template = 'admin/polymorphic/edit_inline/stacked.html'
template = "admin/polymorphic/edit_inline/stacked.html"
+33 -24
View File
@@ -5,7 +5,7 @@ This makes sure that admin fieldsets/layout settings are exported to the templat
"""
import json
from django.contrib.admin.helpers import InlineAdminFormSet, InlineAdminForm, AdminField
from django.contrib.admin.helpers import AdminField, InlineAdminForm, InlineAdminFormSet
from django.utils.encoding import force_text
from django.utils.text import capfirst
from django.utils.translation import ugettext
@@ -19,11 +19,11 @@ class PolymorphicInlineAdminForm(InlineAdminForm):
"""
def polymorphic_ctype_field(self):
return AdminField(self.form, 'polymorphic_ctype', False)
return AdminField(self.form, "polymorphic_ctype", False)
@property
def is_empty(self):
return '__prefix__' in self.form.prefix
return "__prefix__" in self.form.prefix
class PolymorphicInlineAdminFormSet(InlineAdminFormSet):
@@ -32,15 +32,19 @@ class PolymorphicInlineAdminFormSet(InlineAdminFormSet):
"""
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None) # Assigned later via PolymorphicInlineSupportMixin later.
self.obj = kwargs.pop('obj', None)
self.request = kwargs.pop(
"request", None
) # Assigned later via PolymorphicInlineSupportMixin later.
self.obj = kwargs.pop("obj", None)
super(PolymorphicInlineAdminFormSet, self).__init__(*args, **kwargs)
def __iter__(self):
"""
Output all forms using the proper subtype settings.
"""
for form, original in zip(self.formset.initial_forms, self.formset.get_queryset()):
for form, original in zip(
self.formset.initial_forms, self.formset.get_queryset()
):
# Output the form
model = original.get_real_instance_class()
child_inline = self.opts.get_child_inline_instance(model)
@@ -54,7 +58,7 @@ class PolymorphicInlineAdminFormSet(InlineAdminFormSet):
original=original,
readonly_fields=self.get_child_readonly_fields(child_inline),
model_admin=child_inline,
view_on_site_url=view_on_site_url
view_on_site_url=view_on_site_url,
)
# Extra rows, and empty prefixed forms.
@@ -88,22 +92,24 @@ class PolymorphicInlineAdminFormSet(InlineAdminFormSet):
This overrides the default Django version to add the ``childTypes`` data.
"""
verbose_name = self.opts.verbose_name
return json.dumps({
'name': '#%s' % self.formset.prefix,
'options': {
'prefix': self.formset.prefix,
'addText': ugettext('Add another %(verbose_name)s') % {
'verbose_name': capfirst(verbose_name),
return json.dumps(
{
"name": "#%s" % self.formset.prefix,
"options": {
"prefix": self.formset.prefix,
"addText": ugettext("Add another %(verbose_name)s")
% {"verbose_name": capfirst(verbose_name)},
"childTypes": [
{
"type": model._meta.model_name,
"name": force_text(model._meta.verbose_name),
}
for model in self.formset.child_forms.keys()
],
"deleteText": ugettext("Remove"),
},
'childTypes': [
{
'type': model._meta.model_name,
'name': force_text(model._meta.verbose_name)
} for model in self.formset.child_forms.keys()
],
'deleteText': ugettext('Remove'),
}
})
)
class PolymorphicInlineSupportMixin(object):
@@ -119,14 +125,17 @@ class PolymorphicInlineSupportMixin(object):
:class:`~django.contrib.admin.helpers.InlineAdminFormSet` for the polymorphic formsets.
"""
def get_inline_formsets(self, request, formsets, inline_instances, obj=None, *args, **kwargs):
def get_inline_formsets(
self, request, formsets, inline_instances, obj=None, *args, **kwargs
):
"""
Overwritten version to produce the proper admin wrapping for the
polymorphic inline formset. This fixes the media and form appearance
of the inline polymorphic models.
"""
inline_admin_formsets = super(PolymorphicInlineSupportMixin, self).get_inline_formsets(
request, formsets, inline_instances, obj=obj)
inline_admin_formsets = super(
PolymorphicInlineSupportMixin, self
).get_inline_formsets(request, formsets, inline_instances, obj=obj)
for admin_formset in inline_admin_formsets:
if isinstance(admin_formset.formset, BasePolymorphicModelFormSet):
+38 -20
View File
@@ -10,9 +10,14 @@ from django.contrib.admin.utils import flatten_fieldsets
from django.core.exceptions import ImproperlyConfigured
from django.forms import Media
from polymorphic.formsets import polymorphic_child_forms_factory, BasePolymorphicInlineFormSet, \
PolymorphicFormSetChild, UnsupportedChildType
from polymorphic.formsets import (
BasePolymorphicInlineFormSet,
PolymorphicFormSetChild,
UnsupportedChildType,
polymorphic_child_forms_factory,
)
from polymorphic.formsets.utils import add_media
from .helpers import PolymorphicInlineSupportMixin
@@ -31,14 +36,8 @@ class PolymorphicInlineModelAdmin(InlineModelAdmin):
#: The extra media to add for the polymorphic inlines effect.
#: This can be redefined for subclasses.
polymorphic_media = Media(
js=(
'polymorphic/js/polymorphic_inlines.js',
),
css={
'all': (
'polymorphic/css/polymorphic_inlines.css',
)
}
js=("polymorphic/js/polymorphic_inlines.js",),
css={"all": ("polymorphic/css/polymorphic_inlines.css",)},
)
#: The extra forms to show
@@ -90,7 +89,9 @@ class PolymorphicInlineModelAdmin(InlineModelAdmin):
try:
return self._child_inlines_lookup[model]
except KeyError:
raise UnsupportedChildType("Model '{0}' not found in child_inlines".format(model.__name__))
raise UnsupportedChildType(
"Model '{0}' not found in child_inlines".format(model.__name__)
)
def get_formset(self, request, obj=None, **kwargs):
"""
@@ -101,7 +102,9 @@ class PolymorphicInlineModelAdmin(InlineModelAdmin):
:rtype: type
"""
# Construct the FormSet class
FormSet = super(PolymorphicInlineModelAdmin, 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.
@@ -151,7 +154,10 @@ class PolymorphicInlineModelAdmin(InlineModelAdmin):
child_media = child_instance.media
# Avoid adding the same media object again and again
if child_media._css != base_media._css and child_media._js != base_media._js:
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)
@@ -170,12 +176,15 @@ class PolymorphicInlineModelAdmin(InlineModelAdmin):
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(PolymorphicInlineModelAdmin.Child, self).__init__(parent_inline.parent_model, parent_inline.admin_site)
super(PolymorphicInlineModelAdmin.Child, 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,
@@ -209,8 +218,8 @@ class PolymorphicInlineModelAdmin(InlineModelAdmin):
#
# Transfer the local inline attributes to the formset child,
# this allows overriding settings.
if 'fields' in kwargs:
fields = kwargs.pop('fields')
if "fields" in kwargs:
fields = kwargs.pop("fields")
else:
fields = flatten_fieldsets(self.get_fieldsets(request, obj))
@@ -220,9 +229,15 @@ class PolymorphicInlineModelAdmin(InlineModelAdmin):
exclude = list(self.exclude)
exclude.extend(self.get_readonly_fields(request, obj))
exclude.append('polymorphic_ctype') # Django 1.10 blocks it, as it's a readonly field.
exclude.append(
"polymorphic_ctype"
) # Django 1.10 blocks it, as it's a readonly field.
if self.exclude is None and hasattr(self.form, '_meta') and self.form._meta.exclude:
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)
@@ -232,7 +247,9 @@ class PolymorphicInlineModelAdmin(InlineModelAdmin):
"form": self.form,
"fields": fields,
"exclude": exclude or None,
"formfield_callback": partial(self.formfield_for_dbfield, request=request),
"formfield_callback": partial(
self.formfield_for_dbfield, request=request
),
}
defaults.update(kwargs)
@@ -247,5 +264,6 @@ class StackedPolymorphicInline(PolymorphicInlineModelAdmin):
Stacked inline for django-polymorphic models.
Since tabular doesn't make much sense with changed fields, just offer this one.
"""
#: The default template to use.
template = 'admin/polymorphic/edit_inline/stacked.html'
template = "admin/polymorphic/edit_inline/stacked.html"
+102 -60
View File
@@ -7,7 +7,7 @@ from django.contrib import admin
from django.contrib.admin.helpers import AdminErrorList, AdminForm
from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import PermissionDenied, ImproperlyConfigured
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.db import models
from django.http import Http404, HttpResponseRedirect
from django.template.response import TemplateResponse
@@ -15,9 +15,10 @@ from django.utils.encoding import force_text
from django.utils.http import urlencode
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from polymorphic.utils import get_base_polymorphic_model
from .forms import PolymorphicModelChoiceForm
from polymorphic.utils import get_base_polymorphic_model
from .forms import PolymorphicModelChoiceForm
try:
# Django 2.0+
@@ -73,7 +74,9 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
pk_regex = r"(\d+|__fk__)"
def __init__(self, model, admin_site, *args, **kwargs):
super(PolymorphicParentModelAdmin, self).__init__(model, admin_site, *args, **kwargs)
super(PolymorphicParentModelAdmin, self).__init__(
model, admin_site, *args, **kwargs
)
self._is_setup = False
if self.base_model is None:
@@ -105,12 +108,22 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
# After the get_urls() is called, the URLs of the child model can't be exposed anymore to the Django URLconf,
# which also means that a "Save and continue editing" button won't work.
if self._is_setup:
raise RegistrationClosed("The admin model can't be registered anymore at this point.")
raise RegistrationClosed(
"The admin model can't be registered anymore at this point."
)
if not issubclass(model, self.base_model):
raise TypeError("{0} should be a subclass of {1}".format(model.__name__, self.base_model.__name__))
raise TypeError(
"{0} should be a subclass of {1}".format(
model.__name__, self.base_model.__name__
)
)
if not issubclass(model_admin, admin.ModelAdmin):
raise TypeError("{0} should be a subclass of {1}".format(model_admin.__name__, admin.ModelAdmin.__name__))
raise TypeError(
"{0} should be a subclass of {1}".format(
model_admin.__name__, admin.ModelAdmin.__name__
)
)
self._child_admin_site.register(model, model_admin)
@@ -134,7 +147,7 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
self._lazy_setup()
choices = []
for model in self.get_child_models():
perm_function_name = 'has_{0}_permission'.format(action)
perm_function_name = "has_{0}_permission".format(action)
model_admin = self._get_real_admin_by_model(model)
perm_function = getattr(model_admin, perm_function_name)
if not perm_function(request):
@@ -145,21 +158,28 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
def _get_real_admin(self, object_id, super_if_self=True):
try:
obj = self.model.objects.non_polymorphic() \
.values('polymorphic_ctype').get(pk=object_id)
obj = (
self.model.objects.non_polymorphic()
.values("polymorphic_ctype")
.get(pk=object_id)
)
except self.model.DoesNotExist:
raise Http404
return self._get_real_admin_by_ct(obj['polymorphic_ctype'], super_if_self=super_if_self)
return self._get_real_admin_by_ct(
obj["polymorphic_ctype"], super_if_self=super_if_self
)
def _get_real_admin_by_ct(self, ct_id, super_if_self=True):
try:
ct = ContentType.objects.get_for_id(ct_id)
except ContentType.DoesNotExist as e:
raise Http404(e) # Handle invalid GET parameters
raise Http404(e) # Handle invalid GET parameters
model_class = ct.model_class()
if not model_class:
raise Http404("No model found for '{0}.{1}'.".format(*ct.natural_key())) # Handle model deletion
raise Http404(
"No model found for '{0}.{1}'.".format(*ct.natural_key())
) # Handle model deletion
return self._get_real_admin_by_model(model_class, super_if_self=super_if_self)
@@ -167,14 +187,22 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
# In case of a ?ct_id=### parameter, the view is already checked for permissions.
# Hence, make sure this is a derived object, or risk exposing other admin interfaces.
if model_class not in self._child_models:
raise PermissionDenied("Invalid model '{0}', it must be registered as child model.".format(model_class))
raise PermissionDenied(
"Invalid model '{0}', it must be registered as child model.".format(
model_class
)
)
try:
# HACK: the only way to get the instance of an model admin,
# is to read the registry of the AdminSite.
real_admin = self._child_admin_site._registry[model_class]
except KeyError:
raise ChildAdminNotRegistered("No child admin site was registered for a '{0}' model.".format(model_class))
raise ChildAdminNotRegistered(
"No child admin site was registered for a '{0}' model.".format(
model_class
)
)
if super_if_self and real_admin is self:
return super(PolymorphicParentModelAdmin, self)
@@ -188,19 +216,21 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
qs = qs.non_polymorphic()
return qs
def add_view(self, request, form_url='', extra_context=None):
def add_view(self, request, form_url="", extra_context=None):
"""Redirect the add view to the real admin."""
ct_id = int(request.GET.get('ct_id', 0))
ct_id = int(request.GET.get("ct_id", 0))
if not ct_id:
# Display choices
return self.add_type_view(request)
else:
real_admin = self._get_real_admin_by_ct(ct_id)
# rebuild form_url, otherwise libraries below will override it.
form_url = add_preserved_filters({
'preserved_filters': urlencode({'ct_id': ct_id}),
'opts': self.model._meta},
form_url
form_url = add_preserved_filters(
{
"preserved_filters": urlencode({"ct_id": ct_id}),
"opts": self.model._meta,
},
form_url,
)
return real_admin.add_view(request, form_url, extra_context)
@@ -218,7 +248,9 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
return real_admin.changeform_view(request, object_id, *args, **kwargs)
else:
# Add view. As it should already be handled via `add_view`, this means something custom is done here!
return super(PolymorphicParentModelAdmin, self).changeform_view(request, object_id, *args, **kwargs)
return super(PolymorphicParentModelAdmin, self).changeform_view(
request, object_id, *args, **kwargs
)
def history_view(self, request, object_id, extra_context=None):
"""Redirect the history view to the real admin."""
@@ -231,14 +263,14 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
return real_admin.delete_view(request, object_id, extra_context)
def get_preserved_filters(self, request):
if '_changelist_filters' in request.GET:
if "_changelist_filters" in request.GET:
request.GET = request.GET.copy()
filters = request.GET.get('_changelist_filters')
filters = request.GET.get("_changelist_filters")
f = filters.split("&")
for x in f:
c = x.split('=')
c = x.split("=")
request.GET[c[0]] = c[1]
del request.GET['_changelist_filters']
del request.GET["_changelist_filters"]
return super(PolymorphicParentModelAdmin, self).get_preserved_filters(request)
def get_urls(self):
@@ -256,91 +288,100 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
"""
Forward any request to a custom view of the real admin.
"""
ct_id = int(request.GET.get('ct_id', 0))
ct_id = int(request.GET.get("ct_id", 0))
if not ct_id:
# See if the path started with an ID.
try:
pos = path.find('/')
pos = path.find("/")
if pos == -1:
object_id = long(path)
else:
object_id = long(path[0:pos])
except ValueError:
raise Http404("No ct_id parameter, unable to find admin subclass for path '{0}'.".format(path))
raise Http404(
"No ct_id parameter, unable to find admin subclass for path '{0}'.".format(
path
)
)
ct_id = self.model.objects.values_list('polymorphic_ctype_id', flat=True).get(pk=object_id)
ct_id = self.model.objects.values_list(
"polymorphic_ctype_id", flat=True
).get(pk=object_id)
real_admin = self._get_real_admin_by_ct(ct_id)
resolver = URLResolver('^', real_admin.urls)
resolver = URLResolver("^", real_admin.urls)
resolvermatch = resolver.resolve(path) # May raise Resolver404
if not resolvermatch:
raise Http404("No match for path '{0}' in admin subclass.".format(path))
return resolvermatch.func(request, *resolvermatch.args, **resolvermatch.kwargs)
def add_type_view(self, request, form_url=''):
def add_type_view(self, request, form_url=""):
"""
Display a choice form to select which page type to add.
"""
if not self.has_add_permission(request):
raise PermissionDenied
extra_qs = ''
if request.META['QUERY_STRING']:
extra_qs = ""
if request.META["QUERY_STRING"]:
# QUERY_STRING is bytes in Python 3, using force_text() to decode it as string.
# See QueryDict how Django deals with that.
extra_qs = '&{0}'.format(force_text(request.META['QUERY_STRING']))
extra_qs = "&{0}".format(force_text(request.META["QUERY_STRING"]))
choices = self.get_child_type_choices(request, 'add')
choices = self.get_child_type_choices(request, "add")
if len(choices) == 1:
return HttpResponseRedirect('?ct_id={0}{1}'.format(choices[0][0], extra_qs))
return HttpResponseRedirect("?ct_id={0}{1}".format(choices[0][0], extra_qs))
# Create form
form = self.add_type_form(
data=request.POST if request.method == 'POST' else None,
initial={'ct_id': choices[0][0]}
data=request.POST if request.method == "POST" else None,
initial={"ct_id": choices[0][0]},
)
form.fields['ct_id'].choices = choices
form.fields["ct_id"].choices = choices
if form.is_valid():
return HttpResponseRedirect('?ct_id={0}{1}'.format(form.cleaned_data['ct_id'], extra_qs))
return HttpResponseRedirect(
"?ct_id={0}{1}".format(form.cleaned_data["ct_id"], extra_qs)
)
# Wrap in all admin layout
fieldsets = ((None, {'fields': ('ct_id',)}),)
fieldsets = ((None, {"fields": ("ct_id",)}),)
adminForm = AdminForm(form, fieldsets, {}, model_admin=self)
media = self.media + adminForm.media
opts = self.model._meta
context = {
'title': _('Add %s') % force_text(opts.verbose_name),
'adminform': adminForm,
'is_popup': ("_popup" in request.POST or
"_popup" in request.GET),
'media': mark_safe(media),
'errors': AdminErrorList(form, ()),
'app_label': opts.app_label,
"title": _("Add %s") % force_text(opts.verbose_name),
"adminform": adminForm,
"is_popup": ("_popup" in request.POST or "_popup" in request.GET),
"media": mark_safe(media),
"errors": AdminErrorList(form, ()),
"app_label": opts.app_label,
}
return self.render_add_type_form(request, context, form_url)
def render_add_type_form(self, request, context, form_url=''):
def render_add_type_form(self, request, context, form_url=""):
"""
Render the page type choice form.
"""
opts = self.model._meta
app_label = opts.app_label
context.update({
'has_change_permission': self.has_change_permission(request),
'form_url': mark_safe(form_url),
'opts': opts,
'add': True,
'save_on_top': self.save_on_top,
})
context.update(
{
"has_change_permission": self.has_change_permission(request),
"form_url": mark_safe(form_url),
"opts": opts,
"add": True,
"save_on_top": self.save_on_top,
}
)
templates = self.add_type_template or [
"admin/%s/%s/add_type_form.html" % (app_label, opts.object_name.lower()),
"admin/%s/add_type_form.html" % app_label,
"admin/polymorphic/add_type_form.html", # added default here
"admin/add_type_form.html"
"admin/add_type_form.html",
]
request.current_app = self.admin_site.name
@@ -359,7 +400,8 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
"admin/%s/%s/change_list.html" % (app_label, opts.object_name.lower()),
"admin/%s/change_list.html" % app_label,
# Added base class:
"admin/%s/%s/change_list.html" % (base_app_label, base_opts.object_name.lower()),
"admin/%s/%s/change_list.html"
% (base_app_label, base_opts.object_name.lower()),
"admin/%s/change_list.html" % base_app_label,
"admin/change_list.html"
"admin/change_list.html",
]