While implementing a Django CMS site, I’m a little stuck on link management (internal or external). In my project I have a specific box plugin the operator is to use quite often. I added screenshots of edit dialog and box rendering at the end of the question.
The edit dialog is nice, but right now the button is just a char field. So the link selection, either to a page within Django CMS or to an external URL, is missing. I looked for a link field, but until now I am lost.
How to add the feature to let the operator select an internal or external link ?
The plugin consists of these parts:
models.py
from django.db import models
from cms.models.pluginmodel import CMSPlugin
from djangocms_text_ckeditor.fields import HTMLField
class CardPlugin(CMSPlugin):
title = models.CharField(max_length=256,default='')
description = HTMLField(configuration='CKEDITOR_SETTINGS',
default='')
button = models.CharField(max_length=256,default='')
def __str__(self):
return str(self.title)
cms_plugins.py
class CardCMSPlugin(CMSPluginBase):
model = CardPlugin
name = 'Card'
render_template = "card-default.html"
card-default.html
{% load cms_tags %}
<section class="card card--primary-light">
<div class="card__inner">
<div class="card__content">
<div class="card__text">
<h2 class="card__title">
{{ instance.title }}
</h2>
<p class="card__description">
{{ instance.description }}
</p>
</div>
{% if instance.button %}
<div class="card__buttons">
<a href="#0" class="button button--primary-inverse">{{ instance.button }}</a>
</div>
{% endif %}
</div>
</div>
</section>
the rendered box

The plugin edit dialog looks like this:

I'd suggest looking at adding the djangocms-link plugin as a child plugin. It's a very good plugin to link to internal CMS pages or external addresses. That way you could drop the button field from your model, and instead render the child plugins in your template.
The docs on nested plugins would be a good read.
Your plugin definition would become something like this;
class ParentCMSPlugin(CMSPluginBase):
render_template = 'parent.html'
name = 'Parent'
model = ParentPlugin
allow_children = True
child_classes = ['LinkPlugin']
To render child plugins you'd then do this with your template;
{% load cms_tags %}
<section class="card card--primary-light">
<div class="card__inner">
<div class="card__content">
<div class="card__text">
<h2 class="card__title">
{{ instance.title }}
</h2>
<p class="card__description">
{{ instance.description }}
</p>
</div>
{% for plugin in instance.child_plugin_instances %}
{% render_plugin plugin %}
{% endfor %}
</div>
</div>
</section>
And that would use the default render template for the child plugin. If the default didn't match the styling etc, you could handle the rendering in the template instead of using render_plugin or subclass the LinkPlugin to work how you want, or extend it's attributes etc.
Some further consideration should do to dropping your description field in favour of also using the TextPlugin as a child, because the CMS text plugin can nest plugins within itself and is something I'd always use over an HTMLField.
Further still, if you're developing applications hooked in to CMS, take a look at PlaceholderFields which allow you to create placeholders in your own models to hold & use the CMS plugins that you can use in CMS pages. That gets really good for things like news apps or blog style content etc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With