|
@@ -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,
|
|
|
+ )
|
|
|
+
|
|
|
|
|
|
|
|
|
- @classmethod
|
|
|
- def widget_overrides(cls):
|
|
|
+ def widget_overrides(self):
|
|
|
return {}
|
|
|
|
|
|
|
|
|
- @classmethod
|
|
|
- def required_fields(cls):
|
|
|
+ def required_fields(self):
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- @classmethod
|
|
|
- def required_formsets(cls):
|
|
|
+ def required_formsets(self):
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- @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
|
|
|
+
|
|
|
+ new.on_instance_bound()
|
|
|
|
|
|
-
|
|
|
- heading = ""
|
|
|
- help_text = ""
|
|
|
+ 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:
|
|
|
-
|
|
|
- widgets = {}
|
|
|
- for handler_class in cls.children:
|
|
|
- widgets.update(handler_class.widget_overrides())
|
|
|
- cls._widget_overrides = widgets
|
|
|
-
|
|
|
- return cls._widget_overrides
|
|
|
-
|
|
|
- _required_fields = None
|
|
|
-
|
|
|
- @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 cls._required_fields
|
|
|
-
|
|
|
- _required_formsets = None
|
|
|
-
|
|
|
- @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
|
|
|
-
|
|
|
- return cls._required_formsets
|
|
|
|
|
|
- @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)
|
|
|
+ def __init__(self, children=(), *args, **kwargs):
|
|
|
+ super().__init__(*args, **kwargs)
|
|
|
+ self.children = children
|
|
|
|
|
|
- self.children = []
|
|
|
- for child in self.__class__.children:
|
|
|
- if not getattr(child, "children", None) and getattr(child, "field_name", None):
|
|
|
+ def clone(self):
|
|
|
+ return self.__class__(
|
|
|
+ children=self.children,
|
|
|
+ heading=self.heading,
|
|
|
+ classname=self.classname,
|
|
|
+ help_text=self.help_text,
|
|
|
+ )
|
|
|
+
|
|
|
+ def widget_overrides(self):
|
|
|
+
|
|
|
+ widgets = {}
|
|
|
+ for handler_class in self.children:
|
|
|
+ widgets.update(handler_class.widget_overrides())
|
|
|
+ widget_overrides = widgets
|
|
|
+
|
|
|
+ return widget_overrides
|
|
|
+
|
|
|
+ def required_fields(self):
|
|
|
+ fields = []
|
|
|
+ for handler in self.children:
|
|
|
+ fields.extend(handler.required_fields())
|
|
|
+ return fields
|
|
|
+
|
|
|
+ def required_formsets(self):
|
|
|
+ formsets = {}
|
|
|
+ for handler_class in self.children:
|
|
|
+ formsets.update(handler_class.required_formsets())
|
|
|
+ return formsets
|
|
|
+
|
|
|
+ def html_declarations(self):
|
|
|
+ return mark_safe(''.join([c.html_declarations() for c in self.children]))
|
|
|
+
|
|
|
+ def on_model_bound(self):
|
|
|
+ self.children = [child.bind_to_model(self.model)
|
|
|
+ for child in self.children]
|
|
|
+
|
|
|
+ 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):
|
|
|
|
|
|
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:
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- model_form_class = getattr(model, 'base_form_class', WagtailAdminModelForm)
|
|
|
- base_form_class = cls.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
|
|
|
-
|
|
|
-
|
|
|
-class BaseTabbedInterface(BaseFormEditHandler):
|
|
|
+ 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__)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ model_form_class = getattr(self.model, 'base_form_class',
|
|
|
+ WagtailAdminModelForm)
|
|
|
+ base_form_class = self.base_form_class or model_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 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))
|
|
|
|
|
|
|
|
|
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):
|
|
|
- """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, 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 __init__(self, instance=None, form=None):
|
|
|
- super().__init__(instance=instance, form=form)
|
|
|
- self.bound_field = self.form[self.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
|
|
|
+ )
|
|
|
|
|
|
- self.heading = self.bound_field.label
|
|
|
- self.help_text = self.bound_field.help_text
|
|
|
+ def widget_overrides(self):
|
|
|
+ """check if a specific widget has been defined for this field"""
|
|
|
+ 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):
|
|
|
|
|
|
- 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 []
|
|
|
-
|
|
|
-
|
|
|
-class FieldPanel:
|
|
|
- def __init__(self, field_name, classname="", widget=None):
|
|
|
- self.field_name = field_name
|
|
|
- self.classname = classname
|
|
|
- self.widget = widget
|
|
|
+ return [curry(comparator_class, self.db_field)]
|
|
|
+ return []
|
|
|
|
|
|
- def bind_to_model(self, model):
|
|
|
- base = {
|
|
|
- 'model': model,
|
|
|
- 'field_name': self.field_name,
|
|
|
- 'classname': self.classname,
|
|
|
- }
|
|
|
+ def on_model_bound(self):
|
|
|
+ self.db_field = self.model._meta.get_field(self.field_name)
|
|
|
|
|
|
- if self.widget:
|
|
|
- base['widget'] = self.widget
|
|
|
+ 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
|
|
|
|
|
|
- return type(str('_FieldPanel'), (BaseFieldPanel,), base)
|
|
|
+ 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 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):
|
|
|
|
|
|
|
|
|
|
|
|
- return None
|
|
|
+ return
|
|
|
|
|
|
def render_as_field(self):
|
|
|
instance_obj = self.get_chosen_item()
|
|
@@ -571,124 +531,131 @@ 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)}
|
|
|
+ def __init__(self, field_name, page_type=None, can_choose_root=False):
|
|
|
+ super().__init__(field_name=field_name)
|
|
|
+
|
|
|
+ if page_type:
|
|
|
+
|
|
|
+ if not isinstance(page_type, (list, tuple)):
|
|
|
+ page_type = [page_type]
|
|
|
+ else:
|
|
|
+ page_type = []
|
|
|
|
|
|
- @cached_classmethod
|
|
|
- def target_models(cls):
|
|
|
- if cls.page_type:
|
|
|
+ self.page_type = page_type
|
|
|
+ self.can_choose_root = 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 cls.page_type:
|
|
|
+ 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(
|
|
|
- cls.__name__, page_type
|
|
|
+ self.__class__.__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
|
|
|
+ self.__class__.__name__, page_type
|
|
|
)
|
|
|
)
|
|
|
|
|
|
return target_models
|
|
|
- else:
|
|
|
- return [cls.model._meta.get_field(cls.field_name).remote_field.model]
|
|
|
+ return [self.db_field.remote_field.model]
|
|
|
|
|
|
|
|
|
-class PageChooserPanel:
|
|
|
- def __init__(self, field_name, page_type=None, can_choose_root=False):
|
|
|
- self.field_name = field_name
|
|
|
-
|
|
|
- if page_type:
|
|
|
-
|
|
|
- if not isinstance(page_type, (list, tuple)):
|
|
|
- page_type = [page_type]
|
|
|
- else:
|
|
|
- page_type = []
|
|
|
-
|
|
|
- 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,
|
|
|
- })
|
|
|
-
|
|
|
+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
|
|
|
|
|
|
-class BaseInlinePanel(EditHandler):
|
|
|
- @classmethod
|
|
|
- def get_panel_definitions(cls):
|
|
|
+ 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):
|
|
|
|
|
|
- if cls.panels is not None:
|
|
|
- return cls.panels
|
|
|
+ if self.panels is not None:
|
|
|
+ return self.panels
|
|
|
|
|
|
- else:
|
|
|
- return extract_panel_definitions_from_model_class(
|
|
|
- cls.related.related_model,
|
|
|
- exclude=[cls.related.field.name]
|
|
|
- )
|
|
|
-
|
|
|
- _child_edit_handler_class = None
|
|
|
-
|
|
|
- @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()
|
|
|
+ return extract_panel_definitions_from_model_class(
|
|
|
+ self.related.related_model,
|
|
|
+ exclude=[self.related.field.name]
|
|
|
+ )
|
|
|
+
|
|
|
+ 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)
|
|
|
+
|
|
|
+ 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:
|
|
|
|
|
@@ -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))
|
|
|
|
|
|
|
|
|
|
|
@@ -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,
|
|
|
-
|
|
|
-
|
|
|
- 'min_num': self.min_num,
|
|
|
- 'max_num': self.max_num,
|
|
|
- 'classname': self.classname,
|
|
|
- })
|
|
|
-
|
|
|
-
|
|
|
|
|
|
|
|
|
|
|
|
-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)
|
|
|
|
|
|
|
|
|
|
|
@@ -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):
|
|
|
|
|
|
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
|