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 [1] 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”.


Custom Cascade plugins should set the app_label attribute (see below). This is important so migrations for the proxy models generated by Cascade are created in the correct app.

If this attribute is not set, Cascade will default to the left-most part of the plugin’s module path. So if your plugin lives in myapp.cascadeplugins, Cascade will use myapp as the app label. We recommend that you always set app_label explicitly.

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'


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 GlossaryField to the plugin class.

from django.forms import widgets
from cmsplugin_cascade.plugin_base import CascadePluginBase, PartialFormField

class StylishPlugin(CascadePluginBase):
    color = GlossaryField(
        widgets.Select(choices=[('red', 'Red'), ('green', 'Green')]),
        label="Element's Color",
        help_text="Specify the color of the DOM element."

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 GlossaryField accepts five arguments:

  • 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, the name of the form field is used.
  • If created dynamically, a name, otherwise the attribute name 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.

 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.
 The same as Django’s TextInput-widget, but doing field validation. This checks if the entered input data is a valid number.
 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 editor, djangocms-cascade automatically creates a form for each GlossaryField in the plugin’s class. 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'
    field_name = GlossaryField(widgets.TextInput())

The proxy model created for this plugin class, now contains the extra method content(), which for instance may be accessed during template rendering.


<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.


In case your customized plugin requires some Javascript code to improve the editor’s experience, please refer to the section Handling the client side.

Adding extra fields to the model

In rare situations, you might want to add extra fields to the model, which inherit from django.db.models.fields.Field rather than being emulated by a GlossaryField – so to say, you want real database fields.

This can be achieved by creating your own plugin model inheriting from cmsplugin_cascade.models_base.CascadeModelBase and referring to it in your plugin such as:

class MyPluginModel(CascadeModelBase):
    class Meta:
        db_table = 'shop_cart_cascadeelement'
        verbose_name = _("Cart Element")

    byte_val = models.PositiveSmallIntegerField("Byte Value")

class MySpecialPlugin(LinkPluginBase):
    module = 'My Module'
    name = 'My special Plugin'
    model = MyModel

Transparent Plugins

Some of the plugins in Cascade’s ecosystem are considered as transparent. This means that they logically don’t fit into the given grid-system, but should rather be considered as wrappers of other HTML elements.

For example, the Bootstrap Panel can be added as child of a Column. However, it may contain exactly the same plugins, as the Column does. Now, instead of adding the PanelPlugin as a possible parent to all of our existing Bootstrap plugins, we simply declare the Panel as “transparent”. It then behaves as it’s own parent, allowing all plugins as children, which themselves are permitted to be added to that column.

Transparent plugins can be stacked. For example, the Bootstrap Accordion consists of one or more Accordion Panels. Both of them are considered as transparent, which means that we can add all plugins to an Accordion Panels, which we also could add to a Column.

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:


This name is shown in the pull down menu in structure view. There is not default value.


The app_label to use on generated proxy models. This should usually be the same as the app_label of the app that defines the plugin.


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.


Default: True. This differs from CMSPluginBase.

Is it required that this plugin is a child of another plugin? Otherwise the plugin can be added to any placeholder.


Default: None.

A list of Plugin Class Names. If this is set, the plugin may only be added to plugins listed here.


Default: True. This differs from CMSPluginBase.

Can this plugin have child plugins? Or can other plugins be placed inside this plugin?


Default: A list of plugins, which are allowed as children of this plugin. This differs from CMSPluginBase, where this attribute is None.

Do not override this attribute. DjangoCMS-Cascade automatically generates a list of allowed children plugins, by evaluating the list parent_classes from the other plugins in the pool.

Plugins, which are part of the plugin pool, but which do not specify their parents using the list parent_classes, may be added as children to the current plugin by adding them to the attribute 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 parent_classes.


Default: None.

A CSS class which is always added to the wrapping DOM element.


Default: None.

A dictionary of inline styles, which is always added to the wrapping DOM element.


This is a classmethod, which can be added to a plugin to give it a meaningful name.

Its signature is:

def get_identifier(cls, obj):
    return 'A plugin name'

This method shall be used to name the plugin in structured view.


Override the form used by the plugin editor. This must be a class derived from forms.models.ModelForm.


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.

Deprecated attributes

 This list of PartialFormFields had been replaced by arbitrary class attributes of type GlossaryField.

Plugin Permissions

To register (or unregister) a plugin, simply invoke ./manage.py migrate cmsplugin_cascade. This will add (or remove) the content type and the model permissions. We therefore can control in a very fine grained manner, which user or group is allowed to edit which types of plugins.


[1]After having created a customized plugin, it must be registered in Django’s permission system, otherwise only administrators, but no staff users, are allowed to add, change or delete them.