Search code examples
django-cms

What is the proper way to extend/use LinkPluginMixin in DjangoCMS 4


I'd like to make a custom plugin that re-uses the link-capabilites of the plugins included in djangocms_frontend, like for example the LinkPlugin. I couldn't find an example with something similar, so I was trying to make it like one of the bootstrap-plugins (from djangocms_frontend). So, for what it seems, I should use LinkPluginMixin with some extra fields.

For example, let's say I'd need a plugin that has fields for an header1 and a header2 and the fields of a 'link-enabled' plugin.

Here is what I've tried so far:

// on bla/models.py
class Bla(GetLinkMixin, FrontendUIItem):
    header1 = models.CharField(max_length=50, default='')
    header2 = models.CharField(max_length=50, default='')

// on bla/cms_plugins.py
@plugin_pool.register_plugin
class BlaPlugin(LinkPluginMixin, CMSPluginBase):
    model = Bla
    module = _('MyModule')
    name = _('Bla')
    render_template = "bla/bla.html"
    allow_children = False
    fieldsets = [
        (None, {
            'fields': ('header1', 'header2')
        }),
        (
            _("Link settings"),
            {
                "classes": ("collapse",),
                "fields": (
                    ("external_link", "internal_link"),
                    ("mailto", "phone"),
                    ("anchor", "target"),
                ),
            },
        ),
    ] 

The migrations run fine, and the server runs without errors. But when I try to add a new instance of the plugin on a page, I get the error:

FieldError at /en/admin/cms/placeholder/add-plugin/

Unknown field(s) (phone, external_link, internal_link, anchor, mailto, target) specified for Bla. Check fields/fieldsets/exclude attributes of class BlaPlugin.

So what am I doing wrong?


Solution

  • django CMS frontend stores its data in JSON fields. This is handy when CSS frameworks change, since changes do not require new migrations. When creating your own django CMS frontend plugins, you have to also define a form. The fields for the link are collected from a mixin:

        # forms.py
        from django import forms
        from djangocms_frontend.contrib.link.forms import AbstractLinkForm
        from entangled.forms import EntangledModelForm
    
        from .models import Bla
    
        class BlaPluginForm(AbstractLinkForm, EntangledModelForm):
            class Meta:
                model = Bla
                entangled_fields = {
                    "config": [
                        # No own fields to be stored in the config JSON
                     ]
                }
                untangled_fields = ("header1", "header2",)
    

    The form's property link_is_optional determines if form validation should fail if no link is presented.

    You can also add fields to be stored in the config JSON to the form by adding the form fields and listing them in the "config" entry of the entangled_fields meta.

    To activate the form add form = forms.BlaPluginForm to the plugin class.

    I have not tested this with your exact example, please let me know if it works.