Content: Blog

Tutorials

How to Build a Website and Blog with django CMS, Without Knowing Python/Django - Pt. 9

Angelo Dini

July 26, 2016

Index


Part 9: Creating Your First django CMS Plugin

In the previous chapter, 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 6.

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

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. 

If you haven’t set up your project locally, head back to Part 7 and continue from there. 

Part 8 is 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 Divio 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
/Users/username/Sites/<project-name>

Make sure "Auto-sync" is disabled. Inside the project directory, use the following command to create a new Django application:

docker-compose run --rm web python manage.py 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/models.py” 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


@python_2_unicode_compatible
class Social(CMSPlugin):
    label = models.CharField(
        blank=True,
        max_length=200,
    )

    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:

@python_2_unicode_compatible
class Icon(models.Model):
    name = models.CharField(max_length=200)

    def __str__(self):
        return self.name


@python_2_unicode_compatible
class SocialIcon(CMSPlugin):
    icon = models.ForeignKey(Icon)
    url_link = models.URLField()
    link_title = models.CharField(
        blank=True,
        max_length=200,
    )
    extra_classes = models.CharField(
        blank=True,
        max_length=200,
    )

    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 “cms_plugins.py” 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']


plugin_pool.register_plugin(SocialPlugin)
plugin_pool.register_plugin(SocialIconPlugin)

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="{{ plugin.extra_classes }}"{% endif %}>
            {% render_plugin plugin %}
        </li>
    {% endfor %}
</ul>

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="fa fa-circle fa-stack-2x"></i>
        <i class="fa {{ instance.icon }} fa-stack-1x fa-inverse"></i>
    </span>
</a>

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

from django.contrib import admin

from .models import Icon


# Register your models here.
admin.site.register(Icon)

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

INSTALLED_APPS.extend([
    # add your project specific apps here
    'my_custom_page_extension',
    'my_custom_social_addon',
])

If you’ve skipped Part 8, do not add "my_custom_page_extension."

Finally, run the following commands inside your terminal:

docker-compose run --rm web python manage.py 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 manage.py migrate my_custom_social_addon

screen-3.png

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 “cms_plugin.py” 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” 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” 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>
        <a href="#">
            <span class="fa-stack fa-lg">
                <i class="fa fa-circle fa-stack-2x"></i>
                <i class="fa fa-twitter fa-stack-1x fa-inverse"></i>
            </span>
        </a>
    </li>
    ...
</ul>

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 hitting “open site.”

Congratulations! You now have a fully functioning website with everything from the Clean Blog theme integrated. This addon is also available on GitHub.

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

blog comments powered by Disqus

Want to post your article here?

Contact us