In the previous chapters, we learned how to create a Django app in the form of a page extension. One of the main philosophies of Django is to create re-usable applications that can easily be inserted into other projects. 

In this tutorial, we’ll create a Django app that serves django CMS plugins in exactly that manner.

The plugins we’ll be creating will replace the static social icons in the footer of our website:

To do this, we’ll use nested plugins, as seen in the forms used in Part 14.

We’ll be creating a container that can hold a finite numbers of social icons, with an end result that looks like this, when viewing from the structure board:

To achieve this result, we need to think about the fields required to display this data. 

As we progress through the tutorial, you’ll notice a few similarities from Part 16.

First, let’s have a look at the fields we want to add:

  • Icon - the specific icon we want to add, displayed in a dropdown list
  • URL Link - the URL we’ll be redirecting to our social platform
  • Link Title* - the title shown when our icon is hovered over with the mouse
  • Extra Classes* - in case we need custom classes

*These fields are optional

Let’s get started

Head into your Divio App and click Divio Shell. A shell session will open with the current path selected, as shown in the previous tutorial. 

Part 16 and 17 are not mandatory for this section of the tutorial, but we recommend doing it beforehand since we will not explain certain details like Django Models or the Django Administration, again.

Next, navigate to your project folder inside the workspace directory:

cd <project-name>

Replace "<project-name> " with the project you’ve created in the previous tutorials.


Current Directory
You can type “pwd” into your shell to show the name of the current working directory. This will output something like:
⋊> ~/Sites/<project-name> on master ⨯ pwd


docker-compose run --rm web python startapp my_custom_social_addon

This command will generate a Django application located in <your_project_directory>/my_custom_social_addon

You can open your project's directory by running:

open .

A finder window will open with the current directory from within the shell:

Setting Up the Application

The application we just created is almost identical to the my_custom_page_extension  file structure. This is a standard provided by Django.

To continue, open my_custom_social_addon/ and replace the entire contents with the following code:

# -*- coding: utf-8 -*-
from django.db import models
from django.utils.encoding import python_2_unicode_compatible

from cms.models import CMSPlugin

class Social(CMSPlugin):
    label = models.CharField(

    def __str__(self):
        return self.label

This adds the basic models for our first plugin, the Social Plugin wrapper.

Continue by appending the following code to the same file, directly underneath:

class Icon(models.Model):
    name = models.CharField(max_length=200)
    def __str__(self):

class SocialIcon(CMSPlugin):
    icon = models.ForeignKey(Icon)
    url_link = models.URLField()
    link_title = models.CharField(
    extra_classes = models.CharField(

    def __str__(self):
        return self.link_title or self.url_link

    def copy_relations(self, oldinstance):
        # Because we have a ForeignKey (icon), it's required to copy over
        # the reference to the Icon instance to the new plugin.
        self.icon = oldinstance.icon

This adds the basic models for our second plugin, the nested Icon plugin. As mentioned earlier, you’ll notice some similarities to our previous tutorials.


CMS Plugins
CMS Plugins are reusable content containers that can be inserted into django CMS pages (or any content that uses django CMS placeholders). They enable the automatic publishing of information, without further intervention. You can read more about custom plugins, in our documentation.

Now let’s create our plugin that will be recognised by django CMS. 

First, create a file named within my_custom_social_addon. Then, add the following content to the file:

# -*- coding: utf-8 -*-
from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool

from . import models

class SocialPlugin(CMSPluginBase):
    model = models.Social
    name = 'Social Plugin'
    render_template = 'my_custom_social_addon/social.html'
    allow_children = True
    child_classes = ['SocialIconPlugin']

class SocialIconPlugin(CMSPluginBase):
    model = models.SocialIcon
    name = 'Icon'
    render_template = 'my_custom_social_addon/icon.html'
    require_parent = True
    parent_classes = ['SocialPlugin']


This will register two plugins to the django CMS ecosystem: Social Plugin (the wrapper) and Icon (the singular child of “Social Plugin”).

Every django CMS plugin needs to register a template via render_template. This setting points to a path in your Django Application. 

Create the following two empty templates in your project:

  • my_custom_social_addon/templates/my_custom_social_addon/social.html
  • my_custom_social_addon/templates/my_custom_social_addon/icon.html 

Be sure to create these templates in the correct path and pay attention to the nesting: my_custom_social_addon/templates/my_custom_social_addon 

Otherwise, you might encounter a Template Does Not Exist error.

Next, open social.html and add the following content:

{% load cms_tags %}

<ul class="list-inline text-center">
    {% for plugin in instance.child_plugin_instances %}
        <li{% if plugin.extra_classes %} class="list-inline-item {{ plugin.extra_classes }}"{% endif %}>
            {% render_plugin plugin %}
    {% endfor %}

Now open icon.html and add the following content:

<a href="{{ instance.url_link }}"
    {% if instance.link_title %} title="{{ instance.link_title }}"{% endif %}>
    <span class="fa-stack fa-lg">
        <i class="fas fa-circle fa-stack-2x"></i>
        <i class="fab {{ instance.icon }} fa-stack-1x fa-inverse"></i>

We’ll now modify the my_custom_social_addon/ file. Replace the entire contents with the following code:

from django.contrib import admin

from .models import Icon

# Register your models here.

To move forward and generate the migrations, you’ll need to add the my_custom_social_addon application to your installed apps inside like this:

    # add your project specific apps here

If you’ve skipped part 16 and 17, do not add "my_custom_page_extension."

Finally, run the following commands inside your terminal:

docker-compose run --rm web python makemigrations my_custom_social_addon

When running makemigrations be sure to check the output from your console. There should be no error. If this is the case, follow up with:

docker-compose run --rm web python migrate my_custom_social_addon

Open up your local website. If it isn’t running, start the server by running divio project up in your terminal.

Go to your home page and switch to edit mode. Make sure your are in the structure view and click on the “+” symbol within the Content placeholder. This will open the Add plugin modal. 

Search for Social Plugin and click on it.

The Social Plugin is very simple. It allows you to add a label to be more user-friendly when viewing in the structure mode.

Choose a label and click Save.

Once added, your structure view should look similar to this:

Now click on the “+” symbol, located to the right of the Social Plugin and choose Icon. This is the only choice you have for now, as we configured it in our file. 

You should see the following modal:

We currently have three icons on our footer: Twitter, Facebook and GitHub. We need to add each social network as a choice in the Icon dropdown. 

To do this, click on the “+” symbol to the right. A pop up will open. Add “fa-twitter” in the Name field and hit Save

Repeat this process two more times using “fa-facebook-f” and “fa-github” as the names. 

Once this is done, select fa-twitter from the dropdown and define a URL Link. Optionally, you can also define a Link Title

The end result should look something like this:

We’ve now added our first icon. To add the next two, Click on the “+” symbol to the right of Social Plugin, again. This time, select “fa-facebook-f” and “fa-github” from the dropdown. 

Your result should look like this:

If you switch back to Content view, you’ll discover that the added icons are displayed twice. The first set is our dynamically added plugins and the second set is the static html code:

Let’s remedy this by removing the static html markup and replacing it with our plugins. Open /templates/base.html and search for the following code:

<ul class="list-inline text-center">
  <li class="list-inline-item">
    <a href="#">
      <span class="fa-stack fa-lg">
        <i class="fas fa-circle fa-stack-2x"></i>
        <i class="fab fa-twitter fa-stack-1x fa-inverse"></i>

Replace this with the following placeholder:

{% static_placeholder "footer" %}


Placeholders vs. Static Placeholders
The content of the placeholders we’ve encountered so far is different for every page. Sometimes though, you’ll want to have a section on your website that will be the same on every single page, such as a footer block.

You could hard-code your footer into the template, but it would be nicer to be able to manage it through the CMS. This is what static placeholders are for.

Head back to your browser window and reload. Switch back to structure view and drag & drop the “Social Plugin” container into your newly created Footer block:

Now push your progress back to the cloud by running:

git add -A && git commit -am "added social addon" && git push

followed by:

divio project push db

To apply these changes, open your website in the Divio Control Panel and hit Deploy on the Test Server. You can view your changes after the deployment has finished by opening the site.

Congratulations! You now have a fully functioning website with everything from the Clean Blog theme integrated.

Stay tuned for future tutorials in this series, where we’ll continue walking you through additional features of the Divio Cloud.

Did this answer your question?