kopia lustrzana https://github.com/wagtail/wagtail
Simplifies edit handlers by removing redundant classes.
This also allows to provide some missing arguments to panels like PageChooserPanel.pull/4196/head
rodzic
3d945d0255
commit
5fc191b116
|
@ -37,6 +37,7 @@ Changelog
|
|||
* Added `WAGTAILADMIN_NOTIFICATION_INCLUDE_SUPERUSERS` setting to determine whether superusers are included in moderation email notifications (Bruno Alla)
|
||||
* Added a basic Dockerfile to the project template (Tom Dyson)
|
||||
* StreamField blocks now allow custom `get_template` methods for overriding templates in instances (Christopher Bledsoe)
|
||||
* Simplified edit handler API (Florent Osmont, Bertrand Bordage)
|
||||
* Fix: Do not remove stopwords when generating slugs from non-ASCII titles, to avoid issues with incorrect word boundaries (Sævar Öfjörð Magnússon)
|
||||
* Fix: The PostgreSQL search backend now preserves ordering of the `QuerySet` when searching with `order_by_relevance=False` (Bertrand Bordage)
|
||||
* Fix: Using `modeladmin_register` as a decorator no longer replaces the decorated class with `None` (Tim Heap)
|
||||
|
@ -64,6 +65,7 @@ Changelog
|
|||
* Fix: Style of the page unlock button was broken (Bertrand Bordage)
|
||||
* Fix: Admin search no longer floods browser history (Bertrand Bordage)
|
||||
* Fix: Version comparison now handles custom primary keys on inline models correctly (LB (Ben Johnston))
|
||||
* Fixed error when inserting chooser panels into FieldRowPanel (Florent Osmont, Bertrand Bordage)
|
||||
|
||||
|
||||
1.13.1 (17.11.2017)
|
||||
|
|
|
@ -268,6 +268,7 @@ Contributors
|
|||
* misraX
|
||||
* Bruno Alla
|
||||
* Christopher Bledsoe (The Motley Fool)
|
||||
* Florent Osmont
|
||||
|
||||
Translators
|
||||
===========
|
||||
|
|
|
@ -54,6 +54,7 @@ Other features
|
|||
* Added ``WAGTAILADMIN_NOTIFICATION_INCLUDE_SUPERUSERS`` setting to determine whether superusers are included in moderation email notifications (Bruno Alla)
|
||||
* Added a basic Dockerfile to the project template (Tom Dyson)
|
||||
* StreamField blocks now allow custom ``get_template`` methods for overriding templates in instances (Christopher Bledsoe)
|
||||
* Simplified edit handler API (Florent Osmont, Bertrand Bordage)
|
||||
|
||||
|
||||
Bug fixes
|
||||
|
@ -87,6 +88,7 @@ Bug fixes
|
|||
* Style of the page unlock button was broken (Bertrand Bordage)
|
||||
* Admin search no longer floods browser history (Bertrand Bordage)
|
||||
* Version comparison now handles custom primary keys on inline models correctly (LB (Ben Johnston))
|
||||
* Fixed error when inserting chooser panels into FieldRowPanel (Florent Osmont, Bertrand Bordage)
|
||||
|
||||
|
||||
Upgrade considerations
|
||||
|
|
|
@ -60,9 +60,9 @@ def get_form_class_check(app_configs, **kwargs):
|
|||
|
||||
for cls in get_page_models():
|
||||
edit_handler = cls.get_edit_handler()
|
||||
if not issubclass(edit_handler.get_form_class(cls), WagtailAdminPageForm):
|
||||
if not issubclass(edit_handler.get_form_class(), WagtailAdminPageForm):
|
||||
errors.append(Error(
|
||||
"{cls}.get_edit_handler().get_form_class({cls}) does not extend WagtailAdminPageForm".format(
|
||||
"{cls}.get_edit_handler().get_form_class() does not extend WagtailAdminPageForm".format(
|
||||
cls=cls.__name__),
|
||||
hint="Ensure that the EditHandler for {cls} creates a subclass of WagtailAdminPageForm".format(
|
||||
cls=cls.__name__),
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import math
|
||||
import re
|
||||
|
||||
from django import forms
|
||||
|
@ -6,6 +5,7 @@ from django.core.exceptions import ImproperlyConfigured
|
|||
from django.db.models.fields import FieldDoesNotExist
|
||||
from django.forms.models import fields_for_model
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.functional import curry
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
@ -91,43 +91,74 @@ class EditHandler:
|
|||
the EditHandler API
|
||||
"""
|
||||
|
||||
def __init__(self, heading='', classname='', help_text=''):
|
||||
self.heading = heading
|
||||
self.classname = classname
|
||||
self.help_text = help_text
|
||||
|
||||
def clone(self):
|
||||
return self.__class__(
|
||||
heading=self.heading,
|
||||
classname=self.classname,
|
||||
help_text=self.help_text,
|
||||
)
|
||||
|
||||
# return list of widget overrides that this EditHandler wants to be in place
|
||||
# on the form it receives
|
||||
@classmethod
|
||||
def widget_overrides(cls):
|
||||
def widget_overrides(self):
|
||||
return {}
|
||||
|
||||
# return list of fields that this EditHandler expects to find on the form
|
||||
@classmethod
|
||||
def required_fields(cls):
|
||||
def required_fields(self):
|
||||
return []
|
||||
|
||||
# return a dict of formsets that this EditHandler requires to be present
|
||||
# as children of the ClusterForm; the dict is a mapping from relation name
|
||||
# to parameters to be passed as part of get_form_for_model's 'formsets' kwarg
|
||||
@classmethod
|
||||
def required_formsets(cls):
|
||||
def required_formsets(self):
|
||||
return {}
|
||||
|
||||
# return any HTML that needs to be output on the edit page once per edit handler definition.
|
||||
# Typically this will be used to define snippets of HTML within <script type="text/x-template"></script> blocks
|
||||
# for Javascript code to work with.
|
||||
@classmethod
|
||||
def html_declarations(cls):
|
||||
def html_declarations(self):
|
||||
return ''
|
||||
|
||||
def __init__(self, instance=None, form=None):
|
||||
def bind_to_model(self, model):
|
||||
new = self.clone()
|
||||
new.model = model
|
||||
new.on_model_bound()
|
||||
return new
|
||||
|
||||
def on_model_bound(self):
|
||||
pass
|
||||
|
||||
def bind_to_instance(self, instance=None, form=None):
|
||||
new = self.bind_to_model(self.model)
|
||||
|
||||
if not instance:
|
||||
raise ValueError("EditHandler did not receive an instance object")
|
||||
self.instance = instance
|
||||
new.instance = instance
|
||||
|
||||
if not form:
|
||||
raise ValueError("EditHandler did not receive a form object")
|
||||
self.form = form
|
||||
new.form = form
|
||||
|
||||
# Heading / help text to display to the user
|
||||
heading = ""
|
||||
help_text = ""
|
||||
new.on_instance_bound()
|
||||
|
||||
return new
|
||||
|
||||
def on_instance_bound(self):
|
||||
pass
|
||||
|
||||
def __repr__(self):
|
||||
class_name = self.__class__.__name__
|
||||
try:
|
||||
bound_to = force_text(getattr(self, 'instance',
|
||||
getattr(self, 'model')))
|
||||
except AttributeError:
|
||||
return '<%s>' % class_name
|
||||
return '<%s bound to %s>' % (class_name, bound_to)
|
||||
|
||||
def classes(self):
|
||||
"""
|
||||
|
@ -135,15 +166,9 @@ class EditHandler:
|
|||
Subclasses of EditHandler should override this, invoking super().classes() to
|
||||
append more classes specific to the situation.
|
||||
"""
|
||||
|
||||
classes = []
|
||||
|
||||
try:
|
||||
classes.append(self.classname)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
return classes
|
||||
if self.classname:
|
||||
return [self.classname]
|
||||
return []
|
||||
|
||||
def field_type(self):
|
||||
"""
|
||||
|
@ -199,8 +224,7 @@ class EditHandler:
|
|||
"""
|
||||
return mark_safe(self.render_as_object() + self.render_missing_fields())
|
||||
|
||||
@classmethod
|
||||
def get_comparison(cls):
|
||||
def get_comparison(self):
|
||||
return []
|
||||
|
||||
|
||||
|
@ -209,71 +233,70 @@ class BaseCompositeEditHandler(EditHandler):
|
|||
Abstract class for EditHandlers that manage a set of sub-EditHandlers.
|
||||
Concrete subclasses must attach a 'children' property
|
||||
"""
|
||||
_widget_overrides = None
|
||||
|
||||
@classmethod
|
||||
def widget_overrides(cls):
|
||||
if cls._widget_overrides is None:
|
||||
# build a collated version of all its children's widget lists
|
||||
widgets = {}
|
||||
for handler_class in cls.children:
|
||||
widgets.update(handler_class.widget_overrides())
|
||||
cls._widget_overrides = widgets
|
||||
def __init__(self, children=(), *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.children = children
|
||||
|
||||
return cls._widget_overrides
|
||||
def clone(self):
|
||||
return self.__class__(
|
||||
children=self.children,
|
||||
heading=self.heading,
|
||||
classname=self.classname,
|
||||
help_text=self.help_text,
|
||||
)
|
||||
|
||||
_required_fields = None
|
||||
def widget_overrides(self):
|
||||
# build a collated version of all its children's widget lists
|
||||
widgets = {}
|
||||
for handler_class in self.children:
|
||||
widgets.update(handler_class.widget_overrides())
|
||||
widget_overrides = widgets
|
||||
|
||||
@classmethod
|
||||
def required_fields(cls):
|
||||
if cls._required_fields is None:
|
||||
fields = []
|
||||
for handler_class in cls.children:
|
||||
fields.extend(handler_class.required_fields())
|
||||
cls._required_fields = fields
|
||||
return widget_overrides
|
||||
|
||||
return cls._required_fields
|
||||
def required_fields(self):
|
||||
fields = []
|
||||
for handler in self.children:
|
||||
fields.extend(handler.required_fields())
|
||||
return fields
|
||||
|
||||
_required_formsets = None
|
||||
def required_formsets(self):
|
||||
formsets = {}
|
||||
for handler_class in self.children:
|
||||
formsets.update(handler_class.required_formsets())
|
||||
return formsets
|
||||
|
||||
@classmethod
|
||||
def required_formsets(cls):
|
||||
if cls._required_formsets is None:
|
||||
formsets = {}
|
||||
for handler_class in cls.children:
|
||||
formsets.update(handler_class.required_formsets())
|
||||
cls._required_formsets = formsets
|
||||
def html_declarations(self):
|
||||
return mark_safe(''.join([c.html_declarations() for c in self.children]))
|
||||
|
||||
return cls._required_formsets
|
||||
def on_model_bound(self):
|
||||
self.children = [child.bind_to_model(self.model)
|
||||
for child in self.children]
|
||||
|
||||
@classmethod
|
||||
def html_declarations(cls):
|
||||
return mark_safe(''.join([c.html_declarations() for c in cls.children]))
|
||||
|
||||
def __init__(self, instance=None, form=None):
|
||||
super().__init__(instance=instance, form=form)
|
||||
|
||||
self.children = []
|
||||
for child in self.__class__.children:
|
||||
if not getattr(child, "children", None) and getattr(child, "field_name", None):
|
||||
def on_instance_bound(self):
|
||||
children = []
|
||||
for child in self.children:
|
||||
if isinstance(child, FieldPanel):
|
||||
if self.form._meta.exclude:
|
||||
if child.field_name in self.form._meta.exclude:
|
||||
continue
|
||||
if self.form._meta.fields:
|
||||
if child.field_name not in self.form._meta.fields:
|
||||
continue
|
||||
self.children.append(child(instance=self.instance, form=self.form))
|
||||
children.append(child.bind_to_instance(instance=self.instance,
|
||||
form=self.form))
|
||||
self.children = children
|
||||
|
||||
def render(self):
|
||||
return mark_safe(render_to_string(self.template, {
|
||||
'self': self
|
||||
}))
|
||||
|
||||
@classmethod
|
||||
def get_comparison(cls):
|
||||
def get_comparison(self):
|
||||
comparators = []
|
||||
|
||||
for child in cls.children:
|
||||
for child in self.children:
|
||||
comparators.extend(child.get_comparison())
|
||||
|
||||
return comparators
|
||||
|
@ -291,137 +314,94 @@ class BaseFormEditHandler(BaseCompositeEditHandler):
|
|||
# WagtailAdminModelForm
|
||||
base_form_class = None
|
||||
|
||||
_form_class = None
|
||||
|
||||
@classmethod
|
||||
def get_form_class(cls, model):
|
||||
def get_form_class(self):
|
||||
"""
|
||||
Construct a form class that has all the fields and formsets named in
|
||||
the children of this edit handler.
|
||||
"""
|
||||
if cls._form_class is None:
|
||||
# If a custom form class was passed to the EditHandler, use it.
|
||||
# Otherwise, use the base_form_class from the model.
|
||||
# If that is not defined, use WagtailAdminModelForm.
|
||||
model_form_class = getattr(model, 'base_form_class', WagtailAdminModelForm)
|
||||
base_form_class = cls.base_form_class or model_form_class
|
||||
if not hasattr(self, 'model'):
|
||||
raise AttributeError(
|
||||
'%s is not bound to a model yet. Use `.bind_to_model(model)` '
|
||||
'before using this method.' % self.__class__.__name__)
|
||||
# If a custom form class was passed to the EditHandler, use it.
|
||||
# Otherwise, use the base_form_class from the model.
|
||||
# If that is not defined, use WagtailAdminModelForm.
|
||||
model_form_class = getattr(self.model, 'base_form_class',
|
||||
WagtailAdminModelForm)
|
||||
base_form_class = self.base_form_class or model_form_class
|
||||
|
||||
cls._form_class = get_form_for_model(
|
||||
model,
|
||||
form_class=base_form_class,
|
||||
fields=cls.required_fields(),
|
||||
formsets=cls.required_formsets(),
|
||||
widgets=cls.widget_overrides())
|
||||
return cls._form_class
|
||||
return get_form_for_model(
|
||||
self.model,
|
||||
form_class=base_form_class,
|
||||
fields=self.required_fields(),
|
||||
formsets=self.required_formsets(),
|
||||
widgets=self.widget_overrides())
|
||||
|
||||
|
||||
class BaseTabbedInterface(BaseFormEditHandler):
|
||||
class TabbedInterface(BaseFormEditHandler):
|
||||
template = "wagtailadmin/edit_handlers/tabbed_interface.html"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.base_form_class = kwargs.pop('base_form_class', None)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
class TabbedInterface:
|
||||
def __init__(self, children, base_form_class=None):
|
||||
self.children = children
|
||||
self.base_form_class = base_form_class
|
||||
|
||||
def bind_to_model(self, model):
|
||||
return type(str('_TabbedInterface'), (BaseTabbedInterface,), {
|
||||
'model': model,
|
||||
'children': [child.bind_to_model(model) for child in self.children],
|
||||
'base_form_class': self.base_form_class,
|
||||
})
|
||||
def clone(self):
|
||||
new = super().clone()
|
||||
new.base_form_class = self.base_form_class
|
||||
return new
|
||||
|
||||
|
||||
class BaseObjectList(BaseFormEditHandler):
|
||||
class ObjectList(TabbedInterface):
|
||||
template = "wagtailadmin/edit_handlers/object_list.html"
|
||||
|
||||
|
||||
class ObjectList:
|
||||
def __init__(self, children, heading="", classname="",
|
||||
base_form_class=None):
|
||||
self.children = children
|
||||
self.heading = heading
|
||||
self.classname = classname
|
||||
self.base_form_class = base_form_class
|
||||
|
||||
def bind_to_model(self, model):
|
||||
return type(str('_ObjectList'), (BaseObjectList,), {
|
||||
'model': model,
|
||||
'children': [child.bind_to_model(model) for child in self.children],
|
||||
'heading': self.heading,
|
||||
'classname': self.classname,
|
||||
'base_form_class': self.base_form_class,
|
||||
})
|
||||
|
||||
|
||||
class BaseFieldRowPanel(BaseCompositeEditHandler):
|
||||
class FieldRowPanel(BaseCompositeEditHandler):
|
||||
template = "wagtailadmin/edit_handlers/field_row_panel.html"
|
||||
|
||||
def on_instance_bound(self):
|
||||
super().on_instance_bound()
|
||||
|
||||
class FieldRowPanel:
|
||||
def __init__(self, children, classname=""):
|
||||
self.children = children
|
||||
self.classname = classname
|
||||
|
||||
def bind_to_model(self, model):
|
||||
col_count = " col" + str(int(math.floor(12 / len(self.children))))
|
||||
|
||||
col_count = ' col%s' % (12 // len(self.children))
|
||||
# If child panel doesn't have a col# class then append default based on
|
||||
# number of columns
|
||||
for child in self.children:
|
||||
if not re.search(r'\bcol\d+\b', child.classname):
|
||||
child.classname += col_count
|
||||
|
||||
return type(str('_FieldRowPanel'), (BaseFieldRowPanel,), {
|
||||
'model': model,
|
||||
'children': [child.bind_to_model(model) for child in self.children],
|
||||
'classname': self.classname,
|
||||
})
|
||||
|
||||
|
||||
class BaseMultiFieldPanel(BaseCompositeEditHandler):
|
||||
class MultiFieldPanel(BaseCompositeEditHandler):
|
||||
template = "wagtailadmin/edit_handlers/multi_field_panel.html"
|
||||
|
||||
def classes(self):
|
||||
classes = super().classes()
|
||||
classes.append("multi-field")
|
||||
|
||||
return classes
|
||||
|
||||
|
||||
class MultiFieldPanel:
|
||||
def __init__(self, children, heading="", classname=""):
|
||||
self.children = children
|
||||
self.heading = heading
|
||||
self.classname = classname
|
||||
|
||||
def bind_to_model(self, model):
|
||||
return type(str('_MultiFieldPanel'), (BaseMultiFieldPanel,), {
|
||||
'model': model,
|
||||
'children': [child.bind_to_model(model) for child in self.children],
|
||||
'heading': self.heading,
|
||||
'classname': self.classname,
|
||||
})
|
||||
|
||||
|
||||
class BaseFieldPanel(EditHandler):
|
||||
|
||||
class FieldPanel(EditHandler):
|
||||
TEMPLATE_VAR = 'field_panel'
|
||||
|
||||
@classmethod
|
||||
def widget_overrides(cls):
|
||||
def __init__(self, field_name, *args, **kwargs):
|
||||
widget = kwargs.pop('widget', None)
|
||||
if widget is not None:
|
||||
self.widget = widget
|
||||
super().__init__(*args, **kwargs)
|
||||
self.field_name = field_name
|
||||
|
||||
def clone(self):
|
||||
return self.__class__(
|
||||
field_name=self.field_name,
|
||||
widget=self.widget if hasattr(self, 'widget') else None,
|
||||
heading=self.heading,
|
||||
classname=self.classname,
|
||||
help_text=self.help_text
|
||||
)
|
||||
|
||||
def widget_overrides(self):
|
||||
"""check if a specific widget has been defined for this field"""
|
||||
if hasattr(cls, 'widget'):
|
||||
return {cls.field_name: cls.widget}
|
||||
else:
|
||||
return {}
|
||||
|
||||
def __init__(self, instance=None, form=None):
|
||||
super().__init__(instance=instance, form=form)
|
||||
self.bound_field = self.form[self.field_name]
|
||||
|
||||
self.heading = self.bound_field.label
|
||||
self.help_text = self.bound_field.help_text
|
||||
if hasattr(self, 'widget'):
|
||||
return {self.field_name: self.widget}
|
||||
return {}
|
||||
|
||||
def classes(self):
|
||||
classes = super().classes()
|
||||
|
@ -453,25 +433,22 @@ class BaseFieldPanel(EditHandler):
|
|||
field_template = "wagtailadmin/edit_handlers/field_panel_field.html"
|
||||
|
||||
def render_as_field(self):
|
||||
context = {
|
||||
return mark_safe(render_to_string(self.field_template, {
|
||||
'field': self.bound_field,
|
||||
'field_type': self.field_type(),
|
||||
}
|
||||
return mark_safe(render_to_string(self.field_template, context))
|
||||
}))
|
||||
|
||||
@classmethod
|
||||
def required_fields(cls):
|
||||
return [cls.field_name]
|
||||
def required_fields(self):
|
||||
return [self.field_name]
|
||||
|
||||
@classmethod
|
||||
def get_comparison_class(cls):
|
||||
def get_comparison_class(self):
|
||||
# Hide fields with hidden widget
|
||||
widget_override = cls.widget_overrides().get(cls.field_name, None)
|
||||
widget_override = self.widget_overrides().get(self.field_name, None)
|
||||
if widget_override and widget_override.is_hidden:
|
||||
return
|
||||
|
||||
try:
|
||||
field = cls.model._meta.get_field(cls.field_name)
|
||||
field = self.db_field
|
||||
|
||||
if field.choices:
|
||||
return compare.ChoiceFieldComparison
|
||||
|
@ -491,54 +468,37 @@ class BaseFieldPanel(EditHandler):
|
|||
|
||||
return compare.FieldComparison
|
||||
|
||||
@classmethod
|
||||
def get_comparison(cls):
|
||||
comparator_class = cls.get_comparison_class()
|
||||
def get_comparison(self):
|
||||
comparator_class = self.get_comparison_class()
|
||||
|
||||
if comparator_class:
|
||||
field = cls.model._meta.get_field(cls.field_name)
|
||||
return [curry(comparator_class, field)]
|
||||
else:
|
||||
return []
|
||||
return [curry(comparator_class, self.db_field)]
|
||||
return []
|
||||
|
||||
def on_model_bound(self):
|
||||
self.db_field = self.model._meta.get_field(self.field_name)
|
||||
|
||||
def on_instance_bound(self):
|
||||
self.bound_field = self.form[self.field_name]
|
||||
self.heading = self.bound_field.label
|
||||
self.help_text = self.bound_field.help_text
|
||||
|
||||
def __repr__(self):
|
||||
class_name = self.__class__.__name__
|
||||
try:
|
||||
bound_to = force_text(getattr(self, 'instance',
|
||||
getattr(self, 'model')))
|
||||
except AttributeError:
|
||||
return "<%s '%s'>" % (class_name, self.field_name)
|
||||
return "<%s '%s' bound to %s>" % (class_name, self.field_name, bound_to)
|
||||
|
||||
|
||||
class FieldPanel:
|
||||
def __init__(self, field_name, classname="", widget=None):
|
||||
self.field_name = field_name
|
||||
self.classname = classname
|
||||
self.widget = widget
|
||||
|
||||
def bind_to_model(self, model):
|
||||
base = {
|
||||
'model': model,
|
||||
'field_name': self.field_name,
|
||||
'classname': self.classname,
|
||||
}
|
||||
|
||||
if self.widget:
|
||||
base['widget'] = self.widget
|
||||
|
||||
return type(str('_FieldPanel'), (BaseFieldPanel,), base)
|
||||
|
||||
|
||||
class BaseRichTextFieldPanel(BaseFieldPanel):
|
||||
@classmethod
|
||||
def get_comparison_class(cls):
|
||||
class RichTextFieldPanel(FieldPanel):
|
||||
def get_comparison_class(self):
|
||||
return compare.RichTextFieldComparison
|
||||
|
||||
|
||||
class RichTextFieldPanel:
|
||||
def __init__(self, field_name):
|
||||
self.field_name = field_name
|
||||
|
||||
def bind_to_model(self, model):
|
||||
return type(str('_RichTextFieldPanel'), (BaseRichTextFieldPanel,), {
|
||||
'model': model,
|
||||
'field_name': self.field_name,
|
||||
})
|
||||
|
||||
|
||||
class BaseChooserPanel(BaseFieldPanel):
|
||||
class BaseChooserPanel(FieldPanel):
|
||||
"""
|
||||
Abstract superclass for panels that provide a modal interface for choosing (or creating)
|
||||
a database object such as an image, resulting in an ID that is used to populate
|
||||
|
@ -559,7 +519,7 @@ class BaseChooserPanel(BaseFieldPanel):
|
|||
# if the ForeignKey is null=False, Django decides to raise
|
||||
# a DoesNotExist exception here, rather than returning None
|
||||
# like every other unpopulated field type. Yay consistency!
|
||||
return None
|
||||
return
|
||||
|
||||
def render_as_field(self):
|
||||
instance_obj = self.get_chosen_item()
|
||||
|
@ -571,44 +531,11 @@ class BaseChooserPanel(BaseFieldPanel):
|
|||
return mark_safe(render_to_string(self.field_template, context))
|
||||
|
||||
|
||||
class BasePageChooserPanel(BaseChooserPanel):
|
||||
class PageChooserPanel(BaseChooserPanel):
|
||||
object_type_name = "page"
|
||||
|
||||
@classmethod
|
||||
def widget_overrides(cls):
|
||||
return {cls.field_name: widgets.AdminPageChooser(
|
||||
target_models=cls.target_models(),
|
||||
can_choose_root=cls.can_choose_root)}
|
||||
|
||||
@cached_classmethod
|
||||
def target_models(cls):
|
||||
if cls.page_type:
|
||||
target_models = []
|
||||
|
||||
for page_type in cls.page_type:
|
||||
try:
|
||||
target_models.append(resolve_model_string(page_type))
|
||||
except LookupError:
|
||||
raise ImproperlyConfigured(
|
||||
"{0}.page_type must be of the form 'app_label.model_name', given {1!r}".format(
|
||||
cls.__name__, page_type
|
||||
)
|
||||
)
|
||||
except ValueError:
|
||||
raise ImproperlyConfigured(
|
||||
"{0}.page_type refers to model {1!r} that has not been installed".format(
|
||||
cls.__name__, page_type
|
||||
)
|
||||
)
|
||||
|
||||
return target_models
|
||||
else:
|
||||
return [cls.model._meta.get_field(cls.field_name).remote_field.model]
|
||||
|
||||
|
||||
class PageChooserPanel:
|
||||
def __init__(self, field_name, page_type=None, can_choose_root=False):
|
||||
self.field_name = field_name
|
||||
super().__init__(field_name=field_name)
|
||||
|
||||
if page_type:
|
||||
# Convert single string/model into list
|
||||
|
@ -620,75 +547,115 @@ class PageChooserPanel:
|
|||
self.page_type = page_type
|
||||
self.can_choose_root = can_choose_root
|
||||
|
||||
def bind_to_model(self, model):
|
||||
return type(str('_PageChooserPanel'), (BasePageChooserPanel,), {
|
||||
'model': model,
|
||||
'field_name': self.field_name,
|
||||
'page_type': self.page_type,
|
||||
'can_choose_root': self.can_choose_root,
|
||||
})
|
||||
def clone(self):
|
||||
return self.__class__(
|
||||
field_name=self.field_name,
|
||||
page_type=self.page_type,
|
||||
can_choose_root=self.can_choose_root,
|
||||
)
|
||||
|
||||
def widget_overrides(self):
|
||||
return {self.field_name: widgets.AdminPageChooser(
|
||||
target_models=self.target_models(),
|
||||
can_choose_root=self.can_choose_root)}
|
||||
|
||||
def target_models(self):
|
||||
if self.page_type:
|
||||
target_models = []
|
||||
|
||||
for page_type in self.page_type:
|
||||
try:
|
||||
target_models.append(resolve_model_string(page_type))
|
||||
except LookupError:
|
||||
raise ImproperlyConfigured(
|
||||
"{0}.page_type must be of the form 'app_label.model_name', given {1!r}".format(
|
||||
self.__class__.__name__, page_type
|
||||
)
|
||||
)
|
||||
except ValueError:
|
||||
raise ImproperlyConfigured(
|
||||
"{0}.page_type refers to model {1!r} that has not been installed".format(
|
||||
self.__class__.__name__, page_type
|
||||
)
|
||||
)
|
||||
|
||||
return target_models
|
||||
return [self.db_field.remote_field.model]
|
||||
|
||||
|
||||
class BaseInlinePanel(EditHandler):
|
||||
@classmethod
|
||||
def get_panel_definitions(cls):
|
||||
class InlinePanel(EditHandler):
|
||||
def __init__(self, relation_name, panels=None, heading='', label='',
|
||||
min_num=None, max_num=None, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.relation_name = relation_name
|
||||
self.panels = panels
|
||||
self.heading = heading or label
|
||||
self.label = label
|
||||
self.min_num = min_num
|
||||
self.max_num = max_num
|
||||
|
||||
def clone(self):
|
||||
return self.__class__(
|
||||
relation_name=self.relation_name,
|
||||
panels=self.panels,
|
||||
heading=self.heading,
|
||||
label=self.label,
|
||||
help_text=self.help_text,
|
||||
min_num=self.min_num,
|
||||
max_num=self.max_num,
|
||||
classname=self.classname,
|
||||
)
|
||||
|
||||
def get_panel_definitions(self):
|
||||
# Look for a panels definition in the InlinePanel declaration
|
||||
if cls.panels is not None:
|
||||
return cls.panels
|
||||
if self.panels is not None:
|
||||
return self.panels
|
||||
# Failing that, get it from the model
|
||||
else:
|
||||
return extract_panel_definitions_from_model_class(
|
||||
cls.related.related_model,
|
||||
exclude=[cls.related.field.name]
|
||||
)
|
||||
return extract_panel_definitions_from_model_class(
|
||||
self.related.related_model,
|
||||
exclude=[self.related.field.name]
|
||||
)
|
||||
|
||||
_child_edit_handler_class = None
|
||||
def get_child_edit_handler(self):
|
||||
panels = self.get_panel_definitions()
|
||||
child_edit_handler = MultiFieldPanel(panels, heading=self.heading)
|
||||
return child_edit_handler.bind_to_model(self.related.related_model)
|
||||
|
||||
@classmethod
|
||||
def get_child_edit_handler_class(cls):
|
||||
if cls._child_edit_handler_class is None:
|
||||
panels = cls.get_panel_definitions()
|
||||
cls._child_edit_handler_class = MultiFieldPanel(
|
||||
panels,
|
||||
heading=cls.heading
|
||||
).bind_to_model(cls.related.related_model)
|
||||
|
||||
return cls._child_edit_handler_class
|
||||
|
||||
@classmethod
|
||||
def required_formsets(cls):
|
||||
child_edit_handler_class = cls.get_child_edit_handler_class()
|
||||
def required_formsets(self):
|
||||
child_edit_handler = self.get_child_edit_handler()
|
||||
return {
|
||||
cls.relation_name: {
|
||||
'fields': child_edit_handler_class.required_fields(),
|
||||
'widgets': child_edit_handler_class.widget_overrides(),
|
||||
'min_num': cls.min_num,
|
||||
'validate_min': cls.min_num is not None,
|
||||
'max_num': cls.max_num,
|
||||
'validate_max': cls.max_num is not None
|
||||
self.relation_name: {
|
||||
'fields': child_edit_handler.required_fields(),
|
||||
'widgets': child_edit_handler.widget_overrides(),
|
||||
'min_num': self.min_num,
|
||||
'validate_min': self.min_num is not None,
|
||||
'max_num': self.max_num,
|
||||
'validate_max': self.max_num is not None
|
||||
}
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def html_declarations(cls):
|
||||
return cls.get_child_edit_handler_class().html_declarations()
|
||||
def html_declarations(self):
|
||||
return self.get_child_edit_handler().html_declarations()
|
||||
|
||||
@classmethod
|
||||
def get_comparison(cls):
|
||||
field = cls.model._meta.get_field(cls.relation_name)
|
||||
def get_comparison(self):
|
||||
field_comparisons = []
|
||||
|
||||
for panel in cls.get_panel_definitions():
|
||||
field_comparisons.extend(panel.bind_to_model(cls.related.related_model).get_comparison())
|
||||
for panel in self.get_panel_definitions():
|
||||
field_comparisons.extend(
|
||||
panel.bind_to_model(self.related.related_model)
|
||||
.get_comparison())
|
||||
|
||||
return [curry(compare.ChildRelationComparison, field, field_comparisons)]
|
||||
return [curry(compare.ChildRelationComparison, self.db_field,
|
||||
field_comparisons)]
|
||||
|
||||
def __init__(self, instance=None, form=None):
|
||||
super().__init__(instance=instance, form=form)
|
||||
def on_model_bound(self):
|
||||
self.db_field = self.model._meta.get_field(self.relation_name)
|
||||
manager = getattr(self.model, self.relation_name)
|
||||
self.related = manager.rel
|
||||
|
||||
self.formset = form.formsets[self.__class__.relation_name]
|
||||
def on_instance_bound(self):
|
||||
self.formset = self.form.formsets[self.relation_name]
|
||||
|
||||
child_edit_handler_class = self.__class__.get_child_edit_handler_class()
|
||||
self.children = []
|
||||
for subform in self.formset.forms:
|
||||
# override the DELETE field to have a hidden input
|
||||
|
@ -698,9 +665,10 @@ class BaseInlinePanel(EditHandler):
|
|||
if self.formset.can_order:
|
||||
subform.fields['ORDER'].widget = forms.HiddenInput()
|
||||
|
||||
child_edit_handler = self.get_child_edit_handler()
|
||||
self.children.append(
|
||||
child_edit_handler_class(instance=subform.instance, form=subform)
|
||||
)
|
||||
child_edit_handler.bind_to_instance(instance=subform.instance,
|
||||
form=subform))
|
||||
|
||||
# if this formset is valid, it may have been re-ordered; respect that
|
||||
# in case the parent form errored and we need to re-render
|
||||
|
@ -712,7 +680,9 @@ class BaseInlinePanel(EditHandler):
|
|||
if self.formset.can_order:
|
||||
empty_form.fields['ORDER'].widget = forms.HiddenInput()
|
||||
|
||||
self.empty_child = child_edit_handler_class(instance=empty_form.instance, form=empty_form)
|
||||
self.empty_child = self.get_child_edit_handler()
|
||||
self.empty_child = self.empty_child.bind_to_instance(
|
||||
instance=empty_form.instance, form=empty_form)
|
||||
|
||||
template = "wagtailadmin/edit_handlers/inline_panel.html"
|
||||
|
||||
|
@ -733,46 +703,23 @@ class BaseInlinePanel(EditHandler):
|
|||
}))
|
||||
|
||||
|
||||
class InlinePanel:
|
||||
def __init__(self, relation_name, panels=None, classname='', heading='', label='', help_text='', min_num=None, max_num=None):
|
||||
self.relation_name = relation_name
|
||||
self.panels = panels
|
||||
self.heading = heading or label
|
||||
self.label = label
|
||||
self.help_text = help_text
|
||||
self.min_num = min_num
|
||||
self.max_num = max_num
|
||||
self.classname = classname
|
||||
|
||||
def bind_to_model(self, model):
|
||||
related = getattr(model, self.relation_name).rel
|
||||
|
||||
return type(str('_InlinePanel'), (BaseInlinePanel,), {
|
||||
'model': model,
|
||||
'relation_name': self.relation_name,
|
||||
'related': related,
|
||||
'panels': self.panels,
|
||||
'heading': self.heading,
|
||||
'label': self.label,
|
||||
'help_text': self.help_text,
|
||||
# TODO: can we pick this out of the foreign key definition as an alternative?
|
||||
# (with a bit of help from the inlineformset object, as we do for label/heading)
|
||||
'min_num': self.min_num,
|
||||
'max_num': self.max_num,
|
||||
'classname': self.classname,
|
||||
})
|
||||
|
||||
|
||||
# This allows users to include the publishing panel in their own per-model override
|
||||
# without having to write these fields out by hand, potentially losing 'classname'
|
||||
# and therefore the associated styling of the publishing panel
|
||||
def PublishingPanel():
|
||||
return MultiFieldPanel([
|
||||
FieldRowPanel([
|
||||
FieldPanel('go_live_at'),
|
||||
FieldPanel('expire_at'),
|
||||
], classname="label-above"),
|
||||
], ugettext_lazy('Scheduled publishing'), classname="publishing")
|
||||
class PublishingPanel(MultiFieldPanel):
|
||||
def __init__(self, **kwargs):
|
||||
updated_kwargs = {
|
||||
'children': [
|
||||
FieldRowPanel([
|
||||
FieldPanel('go_live_at'),
|
||||
FieldPanel('expire_at'),
|
||||
], classname="label-above"),
|
||||
],
|
||||
'heading': ugettext_lazy('Scheduled publishing'),
|
||||
'classname': 'publishing',
|
||||
}
|
||||
updated_kwargs.update(kwargs)
|
||||
super().__init__(**updated_kwargs)
|
||||
|
||||
|
||||
# Now that we've defined EditHandlers, we can set up wagtailcore.Page to have some.
|
||||
|
@ -815,14 +762,14 @@ def get_edit_handler(cls):
|
|||
if cls.settings_panels:
|
||||
tabs.append(ObjectList(cls.settings_panels, heading=ugettext_lazy('Settings'), classname="settings"))
|
||||
|
||||
EditHandler = TabbedInterface(tabs, base_form_class=cls.base_form_class)
|
||||
return EditHandler.bind_to_model(cls)
|
||||
edit_handler = TabbedInterface(tabs, base_form_class=cls.base_form_class)
|
||||
return edit_handler.bind_to_model(cls)
|
||||
|
||||
|
||||
Page.get_edit_handler = get_edit_handler
|
||||
|
||||
|
||||
class BaseStreamFieldPanel(BaseFieldPanel):
|
||||
class StreamFieldPanel(FieldPanel):
|
||||
def classes(self):
|
||||
classes = super().classes()
|
||||
classes.append("stream-field")
|
||||
|
@ -834,12 +781,10 @@ class BaseStreamFieldPanel(BaseFieldPanel):
|
|||
|
||||
return classes
|
||||
|
||||
@classmethod
|
||||
def html_declarations(cls):
|
||||
return cls.block_def.all_html_declarations()
|
||||
def html_declarations(self):
|
||||
return self.block_def.all_html_declarations()
|
||||
|
||||
@classmethod
|
||||
def get_comparison_class(cls):
|
||||
def get_comparison_class(self):
|
||||
return compare.StreamFieldComparison
|
||||
|
||||
def id_for_label(self):
|
||||
|
@ -847,16 +792,6 @@ class BaseStreamFieldPanel(BaseFieldPanel):
|
|||
# attach the label to any specific one
|
||||
return ""
|
||||
|
||||
|
||||
class StreamFieldPanel:
|
||||
def __init__(self, field_name, classname=''):
|
||||
self.field_name = field_name
|
||||
self.classname = classname
|
||||
|
||||
def bind_to_model(self, model):
|
||||
return type(str('_StreamFieldPanel'), (BaseStreamFieldPanel,), {
|
||||
'model': model,
|
||||
'field_name': self.field_name,
|
||||
'block_def': model._meta.get_field(self.field_name).stream_block,
|
||||
'classname': self.classname,
|
||||
})
|
||||
def on_model_bound(self):
|
||||
super().on_model_bound()
|
||||
self.block_def = self.db_field.stream_block
|
||||
|
|
|
@ -3,7 +3,7 @@ from datetime import date
|
|||
import mock
|
||||
from django import forms
|
||||
from django.core import checks
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
from wagtail.tests.testapp.forms import ValidatedPageForm
|
||||
|
@ -21,6 +21,14 @@ from wagtail.images.edit_handlers import ImageChooserPanel
|
|||
|
||||
|
||||
class TestGetFormForModel(TestCase):
|
||||
def test_get_form_without_model(self):
|
||||
edit_handler = ObjectList()
|
||||
with self.assertRaisesMessage(
|
||||
AttributeError,
|
||||
'ObjectList is not bound to a model yet. '
|
||||
'Use `.bind_to_model(model)` before using this method.'):
|
||||
edit_handler.get_form_class()
|
||||
|
||||
def test_get_form_for_model(self):
|
||||
EventPageForm = get_form_for_model(EventPage, form_class=WagtailAdminPageForm)
|
||||
form = EventPageForm()
|
||||
|
@ -128,8 +136,8 @@ class TestPageEditHandlers(TestCase):
|
|||
"""
|
||||
Forms for pages should have a base class of WagtailAdminPageForm.
|
||||
"""
|
||||
EditHandler = EventPage.get_edit_handler()
|
||||
EventPageForm = EditHandler.get_form_class(EventPage)
|
||||
edit_handler = EventPage.get_edit_handler()
|
||||
EventPageForm = edit_handler.get_form_class()
|
||||
|
||||
# The generated form should inherit from WagtailAdminPageForm
|
||||
self.assertTrue(issubclass(EventPageForm, WagtailAdminPageForm))
|
||||
|
@ -140,8 +148,8 @@ class TestPageEditHandlers(TestCase):
|
|||
ValidatedPage sets a custom base_form_class. This should be used as the
|
||||
base class when constructing a form for ValidatedPages
|
||||
"""
|
||||
EditHandler = ValidatedPage.get_edit_handler()
|
||||
GeneratedValidatedPageForm = EditHandler.get_form_class(ValidatedPage)
|
||||
edit_handler = ValidatedPage.get_edit_handler()
|
||||
GeneratedValidatedPageForm = edit_handler.get_form_class()
|
||||
|
||||
# The generated form should inherit from ValidatedPageForm, because
|
||||
# ValidatedPage.base_form_class == ValidatedPageForm
|
||||
|
@ -159,7 +167,7 @@ class TestPageEditHandlers(TestCase):
|
|||
id='wagtailadmin.E001')
|
||||
|
||||
invalid_edit_handler = checks.Error(
|
||||
"ValidatedPage.get_edit_handler().get_form_class(ValidatedPage) does not extend WagtailAdminPageForm",
|
||||
"ValidatedPage.get_edit_handler().get_form_class() does not extend WagtailAdminPageForm",
|
||||
hint="Ensure that the EditHandler for ValidatedPage creates a subclass of WagtailAdminPageForm",
|
||||
obj=ValidatedPage,
|
||||
id='wagtailadmin.E002')
|
||||
|
@ -180,9 +188,9 @@ class TestPageEditHandlers(TestCase):
|
|||
ValidatedPage.base_form_class, or provide a custom form class for the
|
||||
edit handler. Check the generated form class is of the correct type.
|
||||
"""
|
||||
ValidatedPage.edit_handler = TabbedInterface([])
|
||||
with mock.patch.object(ValidatedPage, 'edit_handler', new=TabbedInterface([]), create=True):
|
||||
form_class = ValidatedPage.get_edit_handler().get_form_class(ValidatedPage)
|
||||
ValidatedPage.edit_handler = TabbedInterface()
|
||||
with mock.patch.object(ValidatedPage, 'edit_handler', new=TabbedInterface(), create=True):
|
||||
form_class = ValidatedPage.get_edit_handler().get_form_class()
|
||||
self.assertTrue(issubclass(form_class, WagtailAdminPageForm))
|
||||
errors = ValidatedPage.check()
|
||||
self.assertEqual(errors, [])
|
||||
|
@ -219,7 +227,7 @@ class TestExtractPanelDefinitionsFromModelClass(TestCase):
|
|||
class TestTabbedInterface(TestCase):
|
||||
def setUp(self):
|
||||
# a custom tabbed interface for EventPage
|
||||
self.EventPageTabbedInterface = TabbedInterface([
|
||||
self.event_page_tabbed_interface = TabbedInterface([
|
||||
ObjectList([
|
||||
FieldPanel('title', widget=forms.Textarea),
|
||||
FieldPanel('date_from'),
|
||||
|
@ -231,7 +239,7 @@ class TestTabbedInterface(TestCase):
|
|||
]).bind_to_model(EventPage)
|
||||
|
||||
def test_get_form_class(self):
|
||||
EventPageForm = self.EventPageTabbedInterface.get_form_class(EventPage)
|
||||
EventPageForm = self.event_page_tabbed_interface.get_form_class()
|
||||
form = EventPageForm()
|
||||
|
||||
# form must include the 'speakers' formset required by the speakers InlinePanel
|
||||
|
@ -241,11 +249,11 @@ class TestTabbedInterface(TestCase):
|
|||
self.assertEqual(type(form.fields['title'].widget), forms.Textarea)
|
||||
|
||||
def test_render(self):
|
||||
EventPageForm = self.EventPageTabbedInterface.get_form_class(EventPage)
|
||||
EventPageForm = self.event_page_tabbed_interface.get_form_class()
|
||||
event = EventPage(title='Abergavenny sheepdog trials')
|
||||
form = EventPageForm(instance=event)
|
||||
|
||||
tabbed_interface = self.EventPageTabbedInterface(
|
||||
tabbed_interface = self.event_page_tabbed_interface.bind_to_instance(
|
||||
instance=event,
|
||||
form=form
|
||||
)
|
||||
|
@ -269,15 +277,15 @@ class TestTabbedInterface(TestCase):
|
|||
|
||||
def test_required_fields(self):
|
||||
# required_fields should report the set of form fields to be rendered recursively by children of TabbedInterface
|
||||
result = set(self.EventPageTabbedInterface.required_fields())
|
||||
result = set(self.event_page_tabbed_interface.required_fields())
|
||||
self.assertEqual(result, set(['title', 'date_from', 'date_to']))
|
||||
|
||||
def test_render_form_content(self):
|
||||
EventPageForm = self.EventPageTabbedInterface.get_form_class(EventPage)
|
||||
EventPageForm = self.event_page_tabbed_interface.get_form_class()
|
||||
event = EventPage(title='Abergavenny sheepdog trials')
|
||||
form = EventPageForm(instance=event)
|
||||
|
||||
tabbed_interface = self.EventPageTabbedInterface(
|
||||
tabbed_interface = self.event_page_tabbed_interface.bind_to_instance(
|
||||
instance=event,
|
||||
form=form
|
||||
)
|
||||
|
@ -293,7 +301,7 @@ class TestTabbedInterface(TestCase):
|
|||
class TestObjectList(TestCase):
|
||||
def setUp(self):
|
||||
# a custom ObjectList for EventPage
|
||||
self.EventPageObjectList = ObjectList([
|
||||
self.event_page_object_list = ObjectList([
|
||||
FieldPanel('title', widget=forms.Textarea),
|
||||
FieldPanel('date_from'),
|
||||
FieldPanel('date_to'),
|
||||
|
@ -301,7 +309,7 @@ class TestObjectList(TestCase):
|
|||
], heading='Event details', classname="shiny").bind_to_model(EventPage)
|
||||
|
||||
def test_get_form_class(self):
|
||||
EventPageForm = self.EventPageObjectList.get_form_class(EventPage)
|
||||
EventPageForm = self.event_page_object_list.get_form_class()
|
||||
form = EventPageForm()
|
||||
|
||||
# form must include the 'speakers' formset required by the speakers InlinePanel
|
||||
|
@ -311,11 +319,11 @@ class TestObjectList(TestCase):
|
|||
self.assertEqual(type(form.fields['title'].widget), forms.Textarea)
|
||||
|
||||
def test_render(self):
|
||||
EventPageForm = self.EventPageObjectList.get_form_class(EventPage)
|
||||
EventPageForm = self.event_page_object_list.get_form_class()
|
||||
event = EventPage(title='Abergavenny sheepdog trials')
|
||||
form = EventPageForm(instance=event)
|
||||
|
||||
object_list = self.EventPageObjectList(
|
||||
object_list = self.event_page_object_list.bind_to_instance(
|
||||
instance=event,
|
||||
form=form
|
||||
)
|
||||
|
@ -345,7 +353,12 @@ class TestFieldPanel(TestCase):
|
|||
self.event = EventPage(title='Abergavenny sheepdog trials',
|
||||
date_from=date(2014, 7, 20), date_to=date(2014, 7, 21))
|
||||
|
||||
self.EndDatePanel = FieldPanel('date_to', classname='full-width').bind_to_model(EventPage)
|
||||
self.end_date_panel = (FieldPanel('date_to', classname='full-width')
|
||||
.bind_to_model(EventPage))
|
||||
|
||||
def test_invalid_field(self):
|
||||
with self.assertRaises(FieldDoesNotExist):
|
||||
FieldPanel('barbecue').bind_to_model(Page)
|
||||
|
||||
def test_render_as_object(self):
|
||||
form = self.EventPageForm(
|
||||
|
@ -354,7 +367,7 @@ class TestFieldPanel(TestCase):
|
|||
|
||||
form.is_valid()
|
||||
|
||||
field_panel = self.EndDatePanel(
|
||||
field_panel = self.end_date_panel.bind_to_instance(
|
||||
instance=self.event,
|
||||
form=form
|
||||
)
|
||||
|
@ -381,7 +394,7 @@ class TestFieldPanel(TestCase):
|
|||
|
||||
form.is_valid()
|
||||
|
||||
field_panel = self.EndDatePanel(
|
||||
field_panel = self.end_date_panel.bind_to_instance(
|
||||
instance=self.event,
|
||||
form=form
|
||||
)
|
||||
|
@ -401,7 +414,7 @@ class TestFieldPanel(TestCase):
|
|||
self.assertNotIn('<p class="error-message">', result)
|
||||
|
||||
def test_required_fields(self):
|
||||
result = self.EndDatePanel.required_fields()
|
||||
result = self.end_date_panel.required_fields()
|
||||
self.assertEqual(result, ['date_to'])
|
||||
|
||||
def test_error_message_is_rendered(self):
|
||||
|
@ -411,7 +424,7 @@ class TestFieldPanel(TestCase):
|
|||
|
||||
form.is_valid()
|
||||
|
||||
field_panel = self.EndDatePanel(
|
||||
field_panel = self.end_date_panel.bind_to_instance(
|
||||
instance=self.event,
|
||||
form=form
|
||||
)
|
||||
|
@ -428,7 +441,7 @@ class TestFieldRowPanel(TestCase):
|
|||
self.event = EventPage(title='Abergavenny sheepdog trials',
|
||||
date_from=date(2014, 7, 20), date_to=date(2014, 7, 21))
|
||||
|
||||
self.DatesPanel = FieldRowPanel([
|
||||
self.dates_panel = FieldRowPanel([
|
||||
FieldPanel('date_from', classname='col4'),
|
||||
FieldPanel('date_to', classname='coltwo'),
|
||||
]).bind_to_model(EventPage)
|
||||
|
@ -440,7 +453,7 @@ class TestFieldRowPanel(TestCase):
|
|||
|
||||
form.is_valid()
|
||||
|
||||
field_panel = self.DatesPanel(
|
||||
field_panel = self.dates_panel.bind_to_instance(
|
||||
instance=self.event,
|
||||
form=form
|
||||
)
|
||||
|
@ -459,7 +472,7 @@ class TestFieldRowPanel(TestCase):
|
|||
|
||||
form.is_valid()
|
||||
|
||||
field_panel = self.DatesPanel(
|
||||
field_panel = self.dates_panel.bind_to_instance(
|
||||
instance=self.event,
|
||||
form=form
|
||||
)
|
||||
|
@ -485,7 +498,7 @@ class TestFieldRowPanel(TestCase):
|
|||
|
||||
form.is_valid()
|
||||
|
||||
field_panel = self.DatesPanel(
|
||||
field_panel = self.dates_panel.bind_to_instance(
|
||||
instance=self.event,
|
||||
form=form
|
||||
)
|
||||
|
@ -501,7 +514,7 @@ class TestFieldRowPanel(TestCase):
|
|||
|
||||
form.is_valid()
|
||||
|
||||
field_panel = self.DatesPanel(
|
||||
field_panel = self.dates_panel.bind_to_instance(
|
||||
instance=self.event,
|
||||
form=form
|
||||
)
|
||||
|
@ -517,7 +530,7 @@ class TestFieldRowPanel(TestCase):
|
|||
|
||||
form.is_valid()
|
||||
|
||||
field_panel = self.DatesPanel(
|
||||
field_panel = self.dates_panel.bind_to_instance(
|
||||
instance=self.event,
|
||||
form=form
|
||||
)
|
||||
|
@ -527,6 +540,38 @@ class TestFieldRowPanel(TestCase):
|
|||
self.assertIn('<li class="field-col col4', result)
|
||||
|
||||
|
||||
class TestFieldRowPanelWithChooser(TestCase):
|
||||
def setUp(self):
|
||||
self.EventPageForm = get_form_for_model(
|
||||
EventPage, form_class=WagtailAdminPageForm, formsets=[])
|
||||
self.event = EventPage(title='Abergavenny sheepdog trials',
|
||||
date_from=date(2014, 7, 19), date_to=date(2014, 7, 21))
|
||||
|
||||
self.dates_panel = FieldRowPanel([
|
||||
FieldPanel('date_from'),
|
||||
ImageChooserPanel('feed_image'),
|
||||
]).bind_to_model(EventPage)
|
||||
|
||||
def test_render_as_object(self):
|
||||
form = self.EventPageForm(
|
||||
{'title': 'Pontypridd sheepdog trials', 'date_from': '2014-07-20', 'date_to': '2014-07-22'},
|
||||
instance=self.event)
|
||||
|
||||
form.is_valid()
|
||||
|
||||
field_panel = self.dates_panel.bind_to_instance(
|
||||
instance=self.event,
|
||||
form=form
|
||||
)
|
||||
result = field_panel.render_as_object()
|
||||
|
||||
# check that the populated form field is included
|
||||
self.assertIn('value="2014-07-20"', result)
|
||||
|
||||
# there should be no errors on this field
|
||||
self.assertNotIn('<p class="error-message">', result)
|
||||
|
||||
|
||||
class TestPageChooserPanel(TestCase):
|
||||
fixtures = ['test.json']
|
||||
|
||||
|
@ -534,11 +579,12 @@ class TestPageChooserPanel(TestCase):
|
|||
model = PageChooserModel # a model with a foreign key to Page which we want to render as a page chooser
|
||||
|
||||
# a PageChooserPanel class that works on PageChooserModel's 'page' field
|
||||
self.EditHandler = ObjectList([PageChooserPanel('page')]).bind_to_model(PageChooserModel)
|
||||
self.MyPageChooserPanel = self.EditHandler.children[0]
|
||||
self.edit_handler = (ObjectList([PageChooserPanel('page')])
|
||||
.bind_to_model(PageChooserModel))
|
||||
self.my_page_chooser_panel = self.edit_handler.children[0]
|
||||
|
||||
# build a form class containing the fields that MyPageChooserPanel wants
|
||||
self.PageChooserForm = self.EditHandler.get_form_class(PageChooserModel)
|
||||
self.PageChooserForm = self.edit_handler.get_form_class()
|
||||
|
||||
# a test instance of PageChooserModel, pointing to the 'christmas' page
|
||||
self.christmas_page = Page.objects.get(slug='christmas')
|
||||
|
@ -546,7 +592,8 @@ class TestPageChooserPanel(TestCase):
|
|||
self.test_instance = model.objects.create(page=self.christmas_page)
|
||||
|
||||
self.form = self.PageChooserForm(instance=self.test_instance)
|
||||
self.page_chooser_panel = self.MyPageChooserPanel(instance=self.test_instance, form=self.form)
|
||||
self.page_chooser_panel = self.my_page_chooser_panel.bind_to_instance(
|
||||
instance=self.test_instance, form=self.form)
|
||||
|
||||
def test_page_chooser_uses_correct_widget(self):
|
||||
self.assertEqual(type(self.form.fields['page'].widget), AdminPageChooser)
|
||||
|
@ -561,14 +608,15 @@ class TestPageChooserPanel(TestCase):
|
|||
def test_render_js_init_with_can_choose_root_true(self):
|
||||
# construct an alternative page chooser panel object, with can_choose_root=True
|
||||
|
||||
MyPageObjectList = ObjectList([
|
||||
my_page_object_list = ObjectList([
|
||||
PageChooserPanel('page', can_choose_root=True)
|
||||
]).bind_to_model(PageChooserModel)
|
||||
MyPageChooserPanel = MyPageObjectList.children[0]
|
||||
PageChooserForm = MyPageObjectList.get_form_class(EventPageChooserModel)
|
||||
my_page_chooser_panel = my_page_object_list.children[0]
|
||||
PageChooserForm = my_page_object_list.get_form_class()
|
||||
|
||||
form = PageChooserForm(instance=self.test_instance)
|
||||
page_chooser_panel = MyPageChooserPanel(instance=self.test_instance, form=form)
|
||||
page_chooser_panel = my_page_chooser_panel.bind_to_instance(
|
||||
instance=self.test_instance, form=form)
|
||||
result = page_chooser_panel.render_as_field()
|
||||
|
||||
# the canChooseRoot flag on createPageChooser should now be true
|
||||
|
@ -592,7 +640,8 @@ class TestPageChooserPanel(TestCase):
|
|||
def test_render_as_empty_field(self):
|
||||
test_instance = PageChooserModel()
|
||||
form = self.PageChooserForm(instance=test_instance)
|
||||
page_chooser_panel = self.MyPageChooserPanel(instance=test_instance, form=form)
|
||||
page_chooser_panel = self.my_page_chooser_panel.bind_to_instance(
|
||||
instance=test_instance, form=form)
|
||||
result = page_chooser_panel.render_as_field()
|
||||
|
||||
self.assertIn('<p class="help">help text</p>', result)
|
||||
|
@ -603,19 +652,21 @@ class TestPageChooserPanel(TestCase):
|
|||
form = self.PageChooserForm({'page': ''}, instance=self.test_instance)
|
||||
self.assertFalse(form.is_valid())
|
||||
|
||||
page_chooser_panel = self.MyPageChooserPanel(instance=self.test_instance, form=form)
|
||||
page_chooser_panel = self.my_page_chooser_panel.bind_to_instance(
|
||||
instance=self.test_instance, form=form)
|
||||
self.assertIn('<span>This field is required.</span>', page_chooser_panel.render_as_field())
|
||||
|
||||
def test_override_page_type(self):
|
||||
# Model has a foreign key to Page, but we specify EventPage in the PageChooserPanel
|
||||
# to restrict the chooser to that page type
|
||||
MyPageObjectList = ObjectList([
|
||||
my_page_object_list = ObjectList([
|
||||
PageChooserPanel('page', 'tests.EventPage')
|
||||
]).bind_to_model(EventPageChooserModel)
|
||||
MyPageChooserPanel = MyPageObjectList.children[0]
|
||||
PageChooserForm = MyPageObjectList.get_form_class(EventPageChooserModel)
|
||||
my_page_chooser_panel = my_page_object_list.children[0]
|
||||
PageChooserForm = my_page_object_list.get_form_class()
|
||||
form = PageChooserForm(instance=self.test_instance)
|
||||
page_chooser_panel = MyPageChooserPanel(instance=self.test_instance, form=form)
|
||||
page_chooser_panel = my_page_chooser_panel.bind_to_instance(
|
||||
instance=self.test_instance, form=form)
|
||||
|
||||
result = page_chooser_panel.render_as_field()
|
||||
expected_js = 'createPageChooser("{id}", ["{model}"], {parent}, false, null);'.format(
|
||||
|
@ -626,11 +677,13 @@ class TestPageChooserPanel(TestCase):
|
|||
def test_autodetect_page_type(self):
|
||||
# Model has a foreign key to EventPage, which we want to autodetect
|
||||
# instead of specifying the page type in PageChooserPanel
|
||||
MyPageObjectList = ObjectList([PageChooserPanel('page')]).bind_to_model(EventPageChooserModel)
|
||||
MyPageChooserPanel = MyPageObjectList.children[0]
|
||||
PageChooserForm = MyPageObjectList.get_form_class(EventPageChooserModel)
|
||||
my_page_object_list = (ObjectList([PageChooserPanel('page')])
|
||||
.bind_to_model(EventPageChooserModel))
|
||||
my_page_chooser_panel = my_page_object_list.children[0]
|
||||
PageChooserForm = my_page_object_list.get_form_class()
|
||||
form = PageChooserForm(instance=self.test_instance)
|
||||
page_chooser_panel = MyPageChooserPanel(instance=self.test_instance, form=form)
|
||||
page_chooser_panel = my_page_chooser_panel.bind_to_instance(
|
||||
instance=self.test_instance, form=form)
|
||||
|
||||
result = page_chooser_panel.render_as_field()
|
||||
expected_js = 'createPageChooser("{id}", ["{model}"], {parent}, false, null);'.format(
|
||||
|
@ -640,14 +693,14 @@ class TestPageChooserPanel(TestCase):
|
|||
|
||||
def test_target_models(self):
|
||||
result = PageChooserPanel(
|
||||
'barbecue',
|
||||
'page',
|
||||
'wagtailcore.site'
|
||||
).bind_to_model(PageChooserModel).target_models()
|
||||
self.assertEqual(result, [Site])
|
||||
|
||||
def test_target_models_malformed_type(self):
|
||||
result = PageChooserPanel(
|
||||
'barbecue',
|
||||
'page',
|
||||
'snowman'
|
||||
).bind_to_model(PageChooserModel)
|
||||
self.assertRaises(ImproperlyConfigured,
|
||||
|
@ -655,7 +708,7 @@ class TestPageChooserPanel(TestCase):
|
|||
|
||||
def test_target_models_nonexistent_type(self):
|
||||
result = PageChooserPanel(
|
||||
'barbecue',
|
||||
'page',
|
||||
'snowman.lorry'
|
||||
).bind_to_model(PageChooserModel)
|
||||
self.assertRaises(ImproperlyConfigured,
|
||||
|
@ -670,10 +723,10 @@ class TestInlinePanel(TestCase, WagtailTestUtils):
|
|||
Check that the inline panel renders the panels set on the model
|
||||
when no 'panels' parameter is passed in the InlinePanel definition
|
||||
"""
|
||||
SpeakerObjectList = ObjectList([
|
||||
speaker_object_list = ObjectList([
|
||||
InlinePanel('speakers', label="Speakers", classname="classname-for-speakers")
|
||||
]).bind_to_model(EventPage)
|
||||
EventPageForm = SpeakerObjectList.get_form_class(EventPage)
|
||||
EventPageForm = speaker_object_list.get_form_class()
|
||||
|
||||
# SpeakerInlinePanel should instruct the form class to include a 'speakers' formset
|
||||
self.assertEqual(['speakers'], list(EventPageForm.formsets.keys()))
|
||||
|
@ -681,7 +734,8 @@ class TestInlinePanel(TestCase, WagtailTestUtils):
|
|||
event_page = EventPage.objects.get(slug='christmas')
|
||||
|
||||
form = EventPageForm(instance=event_page)
|
||||
panel = SpeakerObjectList(instance=event_page, form=form)
|
||||
panel = speaker_object_list.bind_to_instance(instance=event_page,
|
||||
form=form)
|
||||
|
||||
result = panel.render_as_field()
|
||||
|
||||
|
@ -720,22 +774,23 @@ class TestInlinePanel(TestCase, WagtailTestUtils):
|
|||
Check that inline panel renders the panels listed in the InlinePanel definition
|
||||
where one is specified
|
||||
"""
|
||||
SpeakerObjectList = ObjectList([
|
||||
speaker_object_list = ObjectList([
|
||||
InlinePanel('speakers', label="Speakers", panels=[
|
||||
FieldPanel('first_name', widget=forms.Textarea),
|
||||
ImageChooserPanel('image'),
|
||||
]),
|
||||
]).bind_to_model(EventPage)
|
||||
SpeakerInlinePanel = SpeakerObjectList.children[0]
|
||||
EventPageForm = SpeakerObjectList.get_form_class(EventPage)
|
||||
speaker_inline_panel = speaker_object_list.children[0]
|
||||
EventPageForm = speaker_object_list.get_form_class()
|
||||
|
||||
# SpeakerInlinePanel should instruct the form class to include a 'speakers' formset
|
||||
# speaker_inline_panel should instruct the form class to include a 'speakers' formset
|
||||
self.assertEqual(['speakers'], list(EventPageForm.formsets.keys()))
|
||||
|
||||
event_page = EventPage.objects.get(slug='christmas')
|
||||
|
||||
form = EventPageForm(instance=event_page)
|
||||
panel = SpeakerInlinePanel(instance=event_page, form=form)
|
||||
panel = speaker_inline_panel.bind_to_instance(
|
||||
instance=event_page, form=form)
|
||||
|
||||
result = panel.render_as_field()
|
||||
|
||||
|
@ -781,17 +836,18 @@ class TestInlinePanel(TestCase, WagtailTestUtils):
|
|||
https://github.com/wagtail/wagtail/pull/2699
|
||||
https://github.com/wagtail/wagtail/issues/3227
|
||||
"""
|
||||
SpeakerObjectList = ObjectList([
|
||||
speaker_object_list = ObjectList([
|
||||
InlinePanel('speakers', label="Speakers", panels=[
|
||||
FieldPanel('first_name', widget=forms.Textarea),
|
||||
ImageChooserPanel('image'),
|
||||
]),
|
||||
]).bind_to_model(EventPage)
|
||||
SpeakerInlinePanel = SpeakerObjectList.children[0]
|
||||
EventPageForm = SpeakerObjectList.get_form_class(EventPage)
|
||||
speaker_inline_panel = speaker_object_list.children[0]
|
||||
EventPageForm = speaker_object_list.get_form_class()
|
||||
event_page = EventPage.objects.get(slug='christmas')
|
||||
form = EventPageForm(instance=event_page)
|
||||
panel = SpeakerInlinePanel(instance=event_page, form=form)
|
||||
panel = speaker_inline_panel.bind_to_instance(
|
||||
instance=event_page, form=form)
|
||||
|
||||
self.assertIn('maxForms: 1000', panel.render_js_init())
|
||||
|
||||
|
|
|
@ -23,11 +23,11 @@ class BaseRichTextEditHandlerTestCase(TestCase):
|
|||
"""
|
||||
from wagtail.tests.testapp.models import DefaultRichBlockFieldPage
|
||||
|
||||
block_page_edit_handler = DefaultRichBlockFieldPage.get_edit_handler()
|
||||
if block_page_edit_handler._form_class:
|
||||
rich_text_block = block_page_edit_handler._form_class.base_fields['body'].block.child_blocks['rich_text']
|
||||
if hasattr(rich_text_block, 'field'):
|
||||
del rich_text_block.field
|
||||
rich_text_block = (DefaultRichBlockFieldPage.get_edit_handler()
|
||||
.get_form_class().base_fields['body'].block
|
||||
.child_blocks['rich_text'])
|
||||
if hasattr(rich_text_block, 'field'):
|
||||
del rich_text_block.field
|
||||
|
||||
for page_class in get_page_models():
|
||||
page_class.get_edit_handler.cache_clear()
|
||||
|
|
|
@ -183,8 +183,8 @@ def create(request, content_type_app_name, content_type_model_name, parent_page_
|
|||
return result
|
||||
|
||||
page = page_class(owner=request.user)
|
||||
edit_handler_class = page_class.get_edit_handler()
|
||||
form_class = edit_handler_class.get_form_class(page_class)
|
||||
edit_handler = page_class.get_edit_handler()
|
||||
form_class = edit_handler.get_form_class()
|
||||
|
||||
next_url = get_valid_next_url_from_request(request)
|
||||
|
||||
|
@ -270,12 +270,13 @@ def create(request, content_type_app_name, content_type_model_name, parent_page_
|
|||
messages.validation_error(
|
||||
request, _("The page could not be created due to validation errors"), form
|
||||
)
|
||||
edit_handler = edit_handler_class(instance=page, form=form)
|
||||
edit_handler = edit_handler.bind_to_instance(instance=page,
|
||||
form=form)
|
||||
has_unsaved_changes = True
|
||||
else:
|
||||
signals.init_new_page.send(sender=create, page=page, parent=parent_page)
|
||||
form = form_class(instance=page, parent_page=parent_page)
|
||||
edit_handler = edit_handler_class(instance=page, form=form)
|
||||
edit_handler = edit_handler.bind_to_instance(instance=page, form=form)
|
||||
has_unsaved_changes = False
|
||||
|
||||
return render(request, 'wagtailadmin/pages/create.html', {
|
||||
|
@ -307,8 +308,8 @@ def edit(request, page_id):
|
|||
if hasattr(result, 'status_code'):
|
||||
return result
|
||||
|
||||
edit_handler_class = page_class.get_edit_handler()
|
||||
form_class = edit_handler_class.get_form_class(page_class)
|
||||
edit_handler = page_class.get_edit_handler()
|
||||
form_class = edit_handler.get_form_class()
|
||||
|
||||
next_url = get_valid_next_url_from_request(request)
|
||||
|
||||
|
@ -460,7 +461,8 @@ def edit(request, page_id):
|
|||
request, _("The page could not be saved due to validation errors"), form
|
||||
)
|
||||
|
||||
edit_handler = edit_handler_class(instance=page, form=form)
|
||||
edit_handler = edit_handler.bind_to_instance(instance=page,
|
||||
form=form)
|
||||
errors_debug = (
|
||||
repr(edit_handler.form.errors) +
|
||||
repr([
|
||||
|
@ -472,7 +474,7 @@ def edit(request, page_id):
|
|||
has_unsaved_changes = True
|
||||
else:
|
||||
form = form_class(instance=page, parent_page=parent)
|
||||
edit_handler = edit_handler_class(instance=page, form=form)
|
||||
edit_handler = edit_handler.bind_to_instance(instance=page, form=form)
|
||||
has_unsaved_changes = False
|
||||
|
||||
# Check for revisions still undergoing moderation and warn
|
||||
|
@ -564,7 +566,7 @@ class PreviewOnEdit(View):
|
|||
id=self.args[0]).get_latest_revision_as_page()
|
||||
|
||||
def get_form(self, page, query_dict):
|
||||
form_class = page.get_edit_handler().get_form_class(page._meta.model)
|
||||
form_class = page.get_edit_handler().get_form_class()
|
||||
parent_page = page.get_parent().specific
|
||||
|
||||
if self.session_key not in self.request.session:
|
||||
|
@ -1022,11 +1024,12 @@ def revisions_revert(request, page_id, revision_id):
|
|||
content_type = ContentType.objects.get_for_model(page)
|
||||
page_class = content_type.model_class()
|
||||
|
||||
edit_handler_class = page_class.get_edit_handler()
|
||||
form_class = edit_handler_class.get_form_class(page_class)
|
||||
edit_handler = page_class.get_edit_handler()
|
||||
form_class = edit_handler.get_form_class()
|
||||
|
||||
form = form_class(instance=revision_page)
|
||||
edit_handler = edit_handler_class(instance=revision_page, form=form)
|
||||
edit_handler = edit_handler.bind_to_instance(instance=revision_page,
|
||||
form=form)
|
||||
|
||||
user_avatar = render_to_string('wagtailadmin/shared/user_avatar.html', {'user': revision.user})
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ from django.utils.translation import ugettext as _
|
|||
from wagtail.admin.edit_handlers import EditHandler
|
||||
|
||||
|
||||
class BaseFormSubmissionsPanel(EditHandler):
|
||||
class FormSubmissionsPanel(EditHandler):
|
||||
template = "wagtailforms/edit_handlers/form_responses_panel.html"
|
||||
|
||||
def render(self):
|
||||
|
@ -23,14 +23,6 @@ class BaseFormSubmissionsPanel(EditHandler):
|
|||
'last_submit_time': submissions.order_by('submit_time').last().submit_time,
|
||||
}))
|
||||
|
||||
|
||||
class FormSubmissionsPanel:
|
||||
def __init__(self, heading=None):
|
||||
self.heading = heading
|
||||
|
||||
def bind_to_model(self, model):
|
||||
heading = _('{} submissions').format(model.get_verbose_name())
|
||||
return type(str('_FormResponsesPanel'), (BaseFormSubmissionsPanel,), {
|
||||
'model': model,
|
||||
'heading': self.heading or heading,
|
||||
})
|
||||
def on_model_bound(self):
|
||||
if not self.heading:
|
||||
self.heading = _('%s submissions') % self.model.get_verbose_name()
|
||||
|
|
|
@ -29,7 +29,8 @@ class TestFormResponsesPanel(TestCase):
|
|||
|
||||
submissions_panel = FormSubmissionsPanel().bind_to_model(FormPage)
|
||||
|
||||
self.panel = submissions_panel(self.form_page, self.FormPageForm())
|
||||
self.panel = submissions_panel.bind_to_instance(
|
||||
instance=self.form_page, form=self.FormPageForm())
|
||||
|
||||
def test_render_with_submissions(self):
|
||||
"""Show the panel with the count of submission and a link to the list_submissions view."""
|
||||
|
@ -67,7 +68,8 @@ class TestFormResponsesPanelWithCustomSubmissionClass(TestCase):
|
|||
|
||||
submissions_panel = FormSubmissionsPanel().bind_to_model(FormPageWithCustomSubmission)
|
||||
|
||||
self.panel = submissions_panel(self.form_page, self.FormPageForm())
|
||||
self.panel = submissions_panel.bind_to_instance(self.form_page,
|
||||
self.FormPageForm())
|
||||
|
||||
def test_render_with_submissions(self):
|
||||
"""Show the panel with the count of submission and a link to the list_submissions view."""
|
||||
|
|
|
@ -104,7 +104,7 @@ class WMABaseView(TemplateView):
|
|||
|
||||
class ModelFormView(WMABaseView, FormView):
|
||||
|
||||
def get_edit_handler_class(self):
|
||||
def get_edit_handler(self):
|
||||
if hasattr(self.model, 'edit_handler'):
|
||||
edit_handler = self.model.edit_handler
|
||||
else:
|
||||
|
@ -114,7 +114,7 @@ class ModelFormView(WMABaseView, FormView):
|
|||
return edit_handler.bind_to_model(self.model)
|
||||
|
||||
def get_form_class(self):
|
||||
return self.get_edit_handler_class().get_form_class(self.model)
|
||||
return self.get_edit_handler().get_form_class()
|
||||
|
||||
def get_success_url(self):
|
||||
return self.index_url
|
||||
|
@ -136,11 +136,13 @@ class ModelFormView(WMABaseView, FormView):
|
|||
|
||||
def get_context_data(self, **kwargs):
|
||||
instance = self.get_instance()
|
||||
edit_handler_class = self.get_edit_handler_class()
|
||||
edit_handler = self.get_edit_handler()
|
||||
form = self.get_form()
|
||||
edit_handler = edit_handler.bind_to_instance(
|
||||
instance=instance, form=form)
|
||||
context = {
|
||||
'is_multipart': form.is_multipart(),
|
||||
'edit_handler': edit_handler_class(instance=instance, form=form),
|
||||
'edit_handler': edit_handler,
|
||||
'form': form,
|
||||
}
|
||||
context.update(kwargs)
|
||||
|
|
|
@ -9,6 +9,7 @@ from wagtail.contrib.settings.views import get_setting_edit_handler
|
|||
from wagtail.tests.testapp.models import (
|
||||
FileUploadSetting, IconSetting, PanelSettings, TabbedSettings, TestSetting)
|
||||
from wagtail.tests.utils import WagtailTestUtils
|
||||
from wagtail.admin.edit_handlers import FieldPanel, ObjectList, TabbedInterface
|
||||
from wagtail.core import hooks
|
||||
from wagtail.core.models import Page, Site
|
||||
|
||||
|
@ -225,24 +226,24 @@ class TestEditHandlers(TestCase):
|
|||
|
||||
def test_default_model_introspection(self):
|
||||
handler = get_setting_edit_handler(TestSetting)
|
||||
self.assertEqual(handler.__name__, '_ObjectList')
|
||||
self.assertIsInstance(handler, ObjectList)
|
||||
self.assertEqual(len(handler.children), 2)
|
||||
first = handler.children[0]
|
||||
self.assertEqual(first.__name__, '_FieldPanel')
|
||||
self.assertIsInstance(first, FieldPanel)
|
||||
self.assertEqual(first.field_name, 'title')
|
||||
second = handler.children[1]
|
||||
self.assertEqual(second.__name__, '_FieldPanel')
|
||||
self.assertIsInstance(second, FieldPanel)
|
||||
self.assertEqual(second.field_name, 'email')
|
||||
|
||||
def test_with_custom_panels(self):
|
||||
handler = get_setting_edit_handler(PanelSettings)
|
||||
self.assertEqual(handler.__name__, '_ObjectList')
|
||||
self.assertIsInstance(handler, ObjectList)
|
||||
self.assertEqual(len(handler.children), 1)
|
||||
first = handler.children[0]
|
||||
self.assertEqual(first.__name__, '_FieldPanel')
|
||||
self.assertIsInstance(first, FieldPanel)
|
||||
self.assertEqual(first.field_name, 'title')
|
||||
|
||||
def test_with_custom_edit_handler(self):
|
||||
handler = get_setting_edit_handler(TabbedSettings)
|
||||
self.assertEqual(handler.__name__, '_TabbedInterface')
|
||||
self.assertIsInstance(handler, TabbedInterface)
|
||||
self.assertEqual(len(handler.children), 2)
|
||||
|
|
|
@ -8,7 +8,7 @@ from django.utils.translation import ugettext as _
|
|||
|
||||
from wagtail.admin import messages
|
||||
from wagtail.admin.edit_handlers import (
|
||||
ObjectList, extract_panel_definitions_from_model_class)
|
||||
ObjectList, TabbedInterface, extract_panel_definitions_from_model_class)
|
||||
from wagtail.core.models import Site
|
||||
|
||||
from .forms import SiteSwitchForm
|
||||
|
@ -51,8 +51,8 @@ def edit(request, app_name, model_name, site_pk):
|
|||
setting_type_name = model._meta.verbose_name
|
||||
|
||||
instance = model.for_site(site)
|
||||
edit_handler_class = get_setting_edit_handler(model)
|
||||
form_class = edit_handler_class.get_form_class(model)
|
||||
edit_handler = get_setting_edit_handler(model)
|
||||
form_class = edit_handler.get_form_class()
|
||||
|
||||
if request.method == 'POST':
|
||||
form = form_class(request.POST, request.FILES, instance=instance)
|
||||
|
@ -70,10 +70,12 @@ def edit(request, app_name, model_name, site_pk):
|
|||
return redirect('wagtailsettings:edit', app_name, model_name, site.pk)
|
||||
else:
|
||||
messages.error(request, _("The setting could not be saved due to errors."))
|
||||
edit_handler = edit_handler_class(instance=instance, form=form)
|
||||
edit_handler = edit_handler.bind_to_instance(
|
||||
instance=instance, form=form)
|
||||
else:
|
||||
form = form_class(instance=instance)
|
||||
edit_handler = edit_handler_class(instance=instance, form=form)
|
||||
edit_handler = edit_handler.bind_to_instance(
|
||||
instance=instance, form=form)
|
||||
|
||||
# Show a site switcher form if there are multiple sites
|
||||
site_switcher = None
|
||||
|
@ -88,5 +90,5 @@ def edit(request, app_name, model_name, site_pk):
|
|||
'form': form,
|
||||
'site': site,
|
||||
'site_switcher': site_switcher,
|
||||
'tabbed': edit_handler_class.__name__ == '_TabbedInterface',
|
||||
'tabbed': isinstance(edit_handler, TabbedInterface),
|
||||
})
|
||||
|
|
|
@ -3,20 +3,8 @@ from wagtail.admin.edit_handlers import BaseChooserPanel
|
|||
from .widgets import AdminDocumentChooser
|
||||
|
||||
|
||||
class BaseDocumentChooserPanel(BaseChooserPanel):
|
||||
class DocumentChooserPanel(BaseChooserPanel):
|
||||
object_type_name = "document"
|
||||
|
||||
@classmethod
|
||||
def widget_overrides(cls):
|
||||
return {cls.field_name: AdminDocumentChooser}
|
||||
|
||||
|
||||
class DocumentChooserPanel:
|
||||
def __init__(self, field_name):
|
||||
self.field_name = field_name
|
||||
|
||||
def bind_to_model(self, model):
|
||||
return type(str('_DocumentChooserPanel'), (BaseDocumentChooserPanel,), {
|
||||
'model': model,
|
||||
'field_name': self.field_name,
|
||||
})
|
||||
def widget_overrides(self):
|
||||
return {self.field_name: AdminDocumentChooser}
|
||||
|
|
|
@ -6,29 +6,16 @@ from wagtail.admin.edit_handlers import BaseChooserPanel
|
|||
from .widgets import AdminImageChooser
|
||||
|
||||
|
||||
class BaseImageChooserPanel(BaseChooserPanel):
|
||||
class ImageChooserPanel(BaseChooserPanel):
|
||||
object_type_name = "image"
|
||||
|
||||
@classmethod
|
||||
def widget_overrides(cls):
|
||||
return {cls.field_name: AdminImageChooser}
|
||||
def widget_overrides(self):
|
||||
return {self.field_name: AdminImageChooser}
|
||||
|
||||
@classmethod
|
||||
def get_comparison_class(cls):
|
||||
def get_comparison_class(self):
|
||||
return ImageFieldComparison
|
||||
|
||||
|
||||
class ImageChooserPanel:
|
||||
def __init__(self, field_name):
|
||||
self.field_name = field_name
|
||||
|
||||
def bind_to_model(self, model):
|
||||
return type(str('_ImageChooserPanel'), (BaseImageChooserPanel,), {
|
||||
'model': model,
|
||||
'field_name': self.field_name,
|
||||
})
|
||||
|
||||
|
||||
class ImageFieldComparison(ForeignObjectComparison):
|
||||
def htmldiff(self):
|
||||
image_a, image_b = self.get_objects()
|
||||
|
|
|
@ -6,21 +6,11 @@ from wagtail.admin.edit_handlers import BaseChooserPanel
|
|||
from .widgets import AdminSnippetChooser
|
||||
|
||||
|
||||
class BaseSnippetChooserPanel(BaseChooserPanel):
|
||||
class SnippetChooserPanel(BaseChooserPanel):
|
||||
object_type_name = 'item'
|
||||
|
||||
_target_model = None
|
||||
|
||||
@classmethod
|
||||
def widget_overrides(cls):
|
||||
return {cls.field_name: AdminSnippetChooser(model=cls.target_model())}
|
||||
|
||||
@classmethod
|
||||
def target_model(cls):
|
||||
if cls._target_model is None:
|
||||
cls._target_model = cls.model._meta.get_field(cls.field_name).remote_field.model
|
||||
|
||||
return cls._target_model
|
||||
def widget_overrides(self):
|
||||
return {self.field_name: AdminSnippetChooser(model=self.target_model)}
|
||||
|
||||
def render_as_field(self):
|
||||
instance_obj = self.get_chosen_item()
|
||||
|
@ -29,13 +19,6 @@ class BaseSnippetChooserPanel(BaseChooserPanel):
|
|||
self.object_type_name: instance_obj,
|
||||
}))
|
||||
|
||||
|
||||
class SnippetChooserPanel:
|
||||
def __init__(self, field_name):
|
||||
self.field_name = field_name
|
||||
|
||||
def bind_to_model(self, model):
|
||||
return type(str('_SnippetChooserPanel'), (BaseSnippetChooserPanel,), {
|
||||
'model': model,
|
||||
'field_name': self.field_name,
|
||||
})
|
||||
def on_model_bound(self):
|
||||
super().on_model_bound()
|
||||
self.target_model = self.db_field.remote_field.model
|
||||
|
|
|
@ -370,18 +370,18 @@ class TestSnippetChooserPanel(TestCase, WagtailTestUtils):
|
|||
test_snippet = model.objects.create(
|
||||
advert=Advert.objects.create(text=self.advert_text))
|
||||
|
||||
self.edit_handler_class = get_snippet_edit_handler(model)
|
||||
self.form_class = self.edit_handler_class.get_form_class(model)
|
||||
self.edit_handler = get_snippet_edit_handler(model)
|
||||
self.form_class = self.edit_handler.get_form_class()
|
||||
form = self.form_class(instance=test_snippet)
|
||||
edit_handler = self.edit_handler_class(instance=test_snippet, form=form)
|
||||
edit_handler = self.edit_handler.bind_to_instance(instance=test_snippet,
|
||||
form=form)
|
||||
|
||||
self.snippet_chooser_panel = [
|
||||
panel for panel in edit_handler.children
|
||||
if getattr(panel, 'field_name', None) == 'advert'][0]
|
||||
|
||||
def test_create_snippet_chooser_panel_class(self):
|
||||
self.assertEqual(type(self.snippet_chooser_panel).__name__,
|
||||
'_SnippetChooserPanel')
|
||||
self.assertIsInstance(self.snippet_chooser_panel, SnippetChooserPanel)
|
||||
|
||||
def test_render_as_field(self):
|
||||
field_html = self.snippet_chooser_panel.render_as_field()
|
||||
|
@ -392,7 +392,8 @@ class TestSnippetChooserPanel(TestCase, WagtailTestUtils):
|
|||
def test_render_as_empty_field(self):
|
||||
test_snippet = SnippetChooserModel()
|
||||
form = self.form_class(instance=test_snippet)
|
||||
edit_handler = self.edit_handler_class(instance=test_snippet, form=form)
|
||||
edit_handler = self.edit_handler.bind_to_instance(instance=test_snippet,
|
||||
form=form)
|
||||
|
||||
snippet_chooser_panel = [
|
||||
panel for panel in edit_handler.children
|
||||
|
@ -410,7 +411,7 @@ class TestSnippetChooserPanel(TestCase, WagtailTestUtils):
|
|||
def test_target_model_autodetected(self):
|
||||
result = SnippetChooserPanel(
|
||||
'advert'
|
||||
).bind_to_model(SnippetChooserModel).target_model()
|
||||
).bind_to_model(SnippetChooserModel).target_model
|
||||
self.assertEqual(result, Advert)
|
||||
|
||||
|
||||
|
@ -694,14 +695,14 @@ class TestDeleteOnlyPermissions(TestCase, WagtailTestUtils):
|
|||
|
||||
class TestSnippetEditHandlers(TestCase, WagtailTestUtils):
|
||||
def test_standard_edit_handler(self):
|
||||
edit_handler_class = get_snippet_edit_handler(StandardSnippet)
|
||||
form_class = edit_handler_class.get_form_class(StandardSnippet)
|
||||
edit_handler = get_snippet_edit_handler(StandardSnippet)
|
||||
form_class = edit_handler.get_form_class()
|
||||
self.assertTrue(issubclass(form_class, WagtailAdminModelForm))
|
||||
self.assertFalse(issubclass(form_class, FancySnippetForm))
|
||||
|
||||
def test_fancy_edit_handler(self):
|
||||
edit_handler_class = get_snippet_edit_handler(FancySnippet)
|
||||
form_class = edit_handler_class.get_form_class(FancySnippet)
|
||||
edit_handler = get_snippet_edit_handler(FancySnippet)
|
||||
form_class = edit_handler.get_form_class()
|
||||
self.assertTrue(issubclass(form_class, WagtailAdminModelForm))
|
||||
self.assertTrue(issubclass(form_class, FancySnippetForm))
|
||||
|
||||
|
@ -941,18 +942,17 @@ class TestSnippetChooserPanelWithCustomPrimaryKey(TestCase, WagtailTestUtils):
|
|||
)
|
||||
)
|
||||
|
||||
self.edit_handler_class = get_snippet_edit_handler(model)
|
||||
self.form_class = self.edit_handler_class.get_form_class(model)
|
||||
self.edit_handler = get_snippet_edit_handler(model)
|
||||
self.form_class = self.edit_handler.get_form_class()
|
||||
form = self.form_class(instance=test_snippet)
|
||||
edit_handler = self.edit_handler_class(instance=test_snippet, form=form)
|
||||
edit_handler = self.edit_handler.bind_to_instance(instance=test_snippet, form=form)
|
||||
|
||||
self.snippet_chooser_panel = [
|
||||
panel for panel in edit_handler.children
|
||||
if getattr(panel, 'field_name', None) == 'advertwithcustomprimarykey'][0]
|
||||
|
||||
def test_create_snippet_chooser_panel_class(self):
|
||||
self.assertEqual(type(self.snippet_chooser_panel).__name__,
|
||||
'_SnippetChooserPanel')
|
||||
self.assertIsInstance(self.snippet_chooser_panel, SnippetChooserPanel)
|
||||
|
||||
def test_render_as_field(self):
|
||||
field_html = self.snippet_chooser_panel.render_as_field()
|
||||
|
@ -963,7 +963,7 @@ class TestSnippetChooserPanelWithCustomPrimaryKey(TestCase, WagtailTestUtils):
|
|||
def test_render_as_empty_field(self):
|
||||
test_snippet = SnippetChooserModelWithCustomPrimaryKey()
|
||||
form = self.form_class(instance=test_snippet)
|
||||
edit_handler = self.edit_handler_class(instance=test_snippet, form=form)
|
||||
edit_handler = self.edit_handler.bind_to_instance(instance=test_snippet, form=form)
|
||||
|
||||
snippet_chooser_panel = [
|
||||
panel for panel in edit_handler.children
|
||||
|
@ -981,12 +981,10 @@ class TestSnippetChooserPanelWithCustomPrimaryKey(TestCase, WagtailTestUtils):
|
|||
def test_target_model_autodetected(self):
|
||||
result = SnippetChooserPanel(
|
||||
'advertwithcustomprimarykey'
|
||||
).bind_to_model(SnippetChooserModelWithCustomPrimaryKey).target_model()
|
||||
).bind_to_model(SnippetChooserModelWithCustomPrimaryKey).target_model
|
||||
self.assertEqual(result, AdvertWithCustomPrimaryKey)
|
||||
|
||||
|
||||
|
||||
|
||||
class TestSnippetChooseWithCustomPrimaryKey(TestCase, WagtailTestUtils):
|
||||
fixtures = ['test.json']
|
||||
|
||||
|
@ -1014,7 +1012,6 @@ class TestSnippetChooseWithCustomPrimaryKey(TestCase, WagtailTestUtils):
|
|||
self.assertEqual(response.context['items'][0].text, "advert 1")
|
||||
|
||||
|
||||
|
||||
class TestSnippetChosenWithCustomPrimaryKey(TestCase, WagtailTestUtils):
|
||||
fixtures = ['test.json']
|
||||
|
||||
|
|
|
@ -129,8 +129,8 @@ def create(request, app_label, model_name):
|
|||
return permission_denied(request)
|
||||
|
||||
instance = model()
|
||||
edit_handler_class = get_snippet_edit_handler(model)
|
||||
form_class = edit_handler_class.get_form_class(model)
|
||||
edit_handler = get_snippet_edit_handler(model)
|
||||
form_class = edit_handler.get_form_class()
|
||||
|
||||
if request.method == 'POST':
|
||||
form = form_class(request.POST, request.FILES, instance=instance)
|
||||
|
@ -153,10 +153,12 @@ def create(request, app_label, model_name):
|
|||
return redirect('wagtailsnippets:list', app_label, model_name)
|
||||
else:
|
||||
messages.error(request, _("The snippet could not be created due to errors."))
|
||||
edit_handler = edit_handler_class(instance=instance, form=form)
|
||||
edit_handler = edit_handler.bind_to_instance(instance=instance,
|
||||
form=form)
|
||||
else:
|
||||
form = form_class(instance=instance)
|
||||
edit_handler = edit_handler_class(instance=instance, form=form)
|
||||
edit_handler = edit_handler.bind_to_instance(instance=instance,
|
||||
form=form)
|
||||
|
||||
return render(request, 'wagtailsnippets/snippets/create.html', {
|
||||
'model_opts': model._meta,
|
||||
|
@ -173,8 +175,8 @@ def edit(request, app_label, model_name, pk):
|
|||
return permission_denied(request)
|
||||
|
||||
instance = get_object_or_404(model, pk=unquote(pk))
|
||||
edit_handler_class = get_snippet_edit_handler(model)
|
||||
form_class = edit_handler_class.get_form_class(model)
|
||||
edit_handler = get_snippet_edit_handler(model)
|
||||
form_class = edit_handler.get_form_class()
|
||||
|
||||
if request.method == 'POST':
|
||||
form = form_class(request.POST, request.FILES, instance=instance)
|
||||
|
@ -197,10 +199,12 @@ def edit(request, app_label, model_name, pk):
|
|||
return redirect('wagtailsnippets:list', app_label, model_name)
|
||||
else:
|
||||
messages.error(request, _("The snippet could not be saved due to errors."))
|
||||
edit_handler = edit_handler_class(instance=instance, form=form)
|
||||
edit_handler = edit_handler.bind_to_instance(instance=instance,
|
||||
form=form)
|
||||
else:
|
||||
form = form_class(instance=instance)
|
||||
edit_handler = edit_handler_class(instance=instance, form=form)
|
||||
edit_handler = edit_handler.bind_to_instance(instance=instance,
|
||||
form=form)
|
||||
|
||||
return render(request, 'wagtailsnippets/snippets/edit.html', {
|
||||
'model_opts': model._meta,
|
||||
|
|
Ładowanie…
Reference in New Issue