Extending Cascade¶
All Cascade plugins are derived from the same base class CascadeModelBase
, which stores all its
model fields inside a dictionary, serialized as JSON string in the database. This makes it much
easier to extend the Cascade eco-system, since no database migration is required when adding a new,
or extending plugins from this project.
The database model CascadeModelBase
stores all the plugin settings in a single JSON field named
glossary
. This in practice behaves like a Django context, but in order to avoid confusion with
the latter, it has been named “glossary”.
Simple Example¶
This plugin is very simple and just renders static content which has been declared in the template.
from cms.plugin_pool import plugin_pool
from cmsplugin_cascade.plugin_base import CascadePluginBase
class StylishPlugin(CascadePluginBase):
name = 'Stylish Element'
render_template = 'myapp/cascade/stylish-element.html'
plugin_pool.register_plugin(StylishPlugin)
If the editor form pops up for this plugin, a dumb message appears: “There are no further settings for this plugin”. This is because no editable fields have been added to that plugin yet.
Customize Stored Data¶
In order to make the plugin remember its settings and other optional data, the programmer must add a list of special form fields to its plugin. These fields then are used to auto-generate the editor for this DjangoCMS plugin.
Each of those form fields handle a special field value, or in some cases, a list of field values. They all require a widget, which is used when rendering the editors form.
Lets add a simple selector to choose between a red and a green color. Do this by adding a
PartialFormField
to a member list named glossary_fields
.
from django.forms import widgets
from cmsplugin_cascade.plugin_base import CascadePluginBase, PartialFormField
class StylishPlugin(CascadePluginBase):
...
glossary_fields = (
PartialFormField('color',
widgets.Select(choices=(('red', 'Red'), ('green', 'Green'),)),
label="Element's Color",
initial='red',
help_text="Specify the color of the DOM element."
),
# more PartialFormField objects
)
In the plugin’s editor, the form now pops up with a single select box, where the user can choose between a red and a green element.
A PartialFormField
accepts five arguments:
- The name of the field. It must be unique in the given list of
glossary_fields
. - The widget. This can be a built-in Django widget or any valid widget derived from it.
- The
label
used to describe the field. If omitted, thename
of the partial form field is used. - An optional
initial
value to be used with Radio- or Select fields. - An optional
help_text
to describe the field’s purpose.
Widgets for a Partial Form Field¶
For single text fields or select boxes, Django’s built-in widgets, such as widgets.TextInput
or widgets.RadioSelect
can be used. Sometimes these simple widgets are not enough, therefore
some special input widgets have been prepared to be used with DjangoCMS-Cascade. They are all
part of the module cmsplugin_cascade.widgets
.
MultipleTextInputWidget: | |
---|---|
Use this widget to group a list of text input fields together. This for instance is used, to encapsulate all inline styles into one JSON object. | |
NumberInputWidget: | |
The same as Django’s TextInput -widget, but doing field validation. This checks if the
entered input data is a valid number. |
|
MultipleInlineStylesWidget: | |
The same as the MultipleTextInputWidget , but doing field validation. This checks if the
entered input data ends with px or em . |
Overriding the Form¶
For the plugin editor, djangocms-cascade automatically creates a form for each
PartialFormField
in the list of glossary_fields
. Sometimes however, you might need more
control over the fields displayed in the editor, versus the fields stored inside the glossary
.
Similar to the Django’s admin.ModelAdmin
, this can be achieved by overriding the plugins form
element. Such a customized form can add as many fields as required, while the controlled glossary
contains a compact summary.
To override the plugins form, add a member form
to your plugin. This member variable shall refer
to a customized form derived from forms.models.ModelForm
. For further details about how to use
this feature, refer to the supplied implementations.
Overriding the Model¶
Since all djangocms-cascade plugins store their data in a JSON-serializable field, there rarely
is a need to add another database field to the common models CascadeElement
and/or
SharableCascadeElement
and thus no need for database migrations.
However, quite often there is a need to add or override the methods for these models. Therefore each
Cascade plugin creates its own proxy model on the fly. These models are derived from
CascadeElement
and/or SharableCascadeElement
and named like the plugin class, with the
suffix Model
. By default, their behavior is the same as for their parent model classes.
To extend this behavior, the author of a plugin may declare a tuple of mixin classes, which are injected during the creation of the proxy model. Example:
class MySpecialPropertyMixin(object):
def processed_value(self):
value = self.glossary.get('field_name')
# process value
return value
class MySpecialPlugin(LinkPluginBase):
module = 'My Module'
name = 'My special Plugin'
model_mixins = (MySpecialPropertyMixin,)
render_template = 'my_module/my_special_plugin.html'
glossary_fields = (
PartialFormField('field_name',
widgets.TextInput(),
),
# other partial form fields
)
...
The proxy model created for this plugin class, now contains the extra method content()
, which
for instance may be accessed during template rendering.
templates/my_module/my_special_plugin.html
:
<div>{{ instance.processed_value }}</div>
Needless to say, that you can’t add any extra database fields to the class named
MySpecialPropertyMixin
, since the corresponding model class is marked as proxy.
Plugin Attribute Reference¶
CascadePluginBase
is derived from CMSPluginBase
, so all CMSPluginBase attributes can
also be overridden by plugins derived from CascadePluginBase
. Please refer to their
documentation for details.
Additionally BootstrapPluginBase
allows the following attributes:
name: | This name is shown in the pull down menu in structure view. There is not default value. |
---|---|
tag_type: | A HTML element into which this plugin is wrapped. Generic templates can render their content into any ``tag_type. Specialized rendering templates usually have a hard coded tag type, then this attribute can be omitted. |
require_parent: | Default: Is it required that this plugin is a child of another plugin? Otherwise the plugin can be added to any placeholder. |
parent_classes: | Default: None. A list of Plugin Class Names. If this is set, the plugin may only be added to plugins listed here. |
allow_children: | Default: Can this plugin have child plugins? Or can other plugins be placed inside this plugin? |
child_classes: | Default: A list of plugins, which are allowed as children of this plugin. This differs from
Do not override this attribute. DjangoCMS-Cascade automatically generates a list of allowed
children plugins, by evaluating the list Plugins, which are part of the plugin pool, but which do not specify their parents using the
list |
generic_child_classes: | |
Default: None. A list of plugins which shall be added as children to a plugin, but which themselves do not
declare this plugin in their |
|
glossary_fields: | |
Default: None A list of |
|
default_css_class: | |
Default: None. A CSS class which is always added to the wrapping DOM element. |
|
default_inline_styles: | |
Default: None. A dictionary of inline styles, which is always added to the wrapping DOM element. |
|
get_identifier: | This is a classmethod, which can be added to a plugin to give it a meaningful name. Its signature is: @classmethod
def get_identifier(cls, obj):
return 'A plugin name'
This method shall be used to name the plugin in structured view. |
form: | Override the form used by the plugin editor. This must be a class derived from
|
model_mixins: | Tuple of mixin classes, with additional methods to be added the auto-generated proxy model for the given plugin class. Check section “Overriding the Model” for a detailed explanation. |