The django CMS is a highly extensible software. It allows you to create/extend custom Page or Title models, dynamic CMS Addons, but also defines custom Toolbar behaviours. 

To get started, head back to your favourite editor and open the local project now.

Once you have everything set, open “my_custom_page_extension/models.py” and replace the entire contents with the following code:

from django.db import models

from cms.extensions import PageExtension
from cms.extensions.extension_pool import extension_pool

from filer.fields.image import FilerImageField


class PageFieldExtension(PageExtension):
    subtitle = models.CharField(max_length=255, blank=True)
    background_image = FilerImageField(null=True, blank=True)

extension_pool.register(PageFieldExtension)

___

Django Models
Django models are descriptions of how data is stored in the database as code. Traditionally, you would create the database structure through SQL commands or a UI tool such as phpMyAdmin. Django does this through your code. Let’s have a look at an example:

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    birthday = models.DateField()

This code tells your database to create a table “Person” with three fields named “first_name,” “last_name” and “birthday.” Each of the columns can have certain properties. “First_name,” for example, is a “Character Field” that allows you to store up to 50 characters. There are many more options available.
___

Save the file and create a new one named “cms_toolbars.py” inside “my_custom_page_extension.” Copy and paste the following code in there:

# page extension imports
from cms.extensions.toolbar import ExtensionToolbar
from cms.toolbar_pool import toolbar_pool

# import our model from ``models.py``
from .models import PageFieldExtension


@toolbar_pool.register
class PageFieldExtensionToolbar(ExtensionToolbar):
    # loads the modal from ``models.py``
    model = PageFieldExtension

    def populate(self):
        # setup the extension toolbar with permissions and sanity checks
        current_page_menu = self._setup_extension_toolbar()
        # if its all ok
        if current_page_menu:
            # retrieves the instance of the current extension (if any)
            # and the toolbar item URL
            page_extension, url = self.get_page_extension_admin()
            if url:
                # adds separator
                current_page_menu.add_break()
                # adds a toolbar item
                current_page_menu.add_modal_item(
                    'Extra settings',
                    url=url,
                    disabled=not self.toolbar.edit_mode,
                )

___

Toolbar API
The django CMS toolbar is a core part of the user experience. Because of this, it provides an extensive API to add, modify and remove menu and button entries. This can be as simple as a link to an external website or complex entries that change their behaviour, according to your actions.
___

Next, open “my_custom_page_extension/admin.py” and replace its contents with:

from django.contrib import admin

# page extension imports
from cms.extensions import PageExtensionAdmin

# import our model from ``models.py``
from .models import PageFieldExtension


class PageFieldExtensionAdmin(PageExtensionAdmin):
    pass

admin.site.register(PageFieldExtension, PageFieldExtensionAdmin)

___

Django Administration
Creating models as described in “Django Models” is just one step. The next requires you to register it to the administration in order to be editable as a user. Django admin is very powerful and provides automatic display and validation, according to their properties. Take a look at:

first_name = models.CharField(max_length=50)

In this example, Django will automatically recognise the character field as a text input: “<input type="text"> ” and adds the necessary restrictions: “<input type="text" maxlength="50"> .” Furthermore, the admin provides a nice user interface to display the fields.
___

Now you need to add our application to Django’s “INSTALLED_APPS” section. Open up “/settings.py” inside the project root and replace:

INSTALLED_APPS.extend([
    # add you project specific apps here
])

with the following code:

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

Next, we need to create database migrations for our application, so Django knows which tables and fields need to be added to the database.

Enter the following command into your terminal:

docker-compose run --rm web python manage.py makemigrations my_custom_page_extension

This command will prepare all “migrations” defined in our Django models.

Follow up with:

docker-compose run --rm web python manage.py migrate my_custom_page_extension

This command will take the prepared migrations and apply them to the database. Be sure to check the output from your console. There should be no error as shown in the image above.

___

Django Migrations
Writing a model in “models.py” does not automatically create the tables and fields in your database. You first need to create a schema using “makemigrations” that will track changes over time.

As your application evolves, so does your data. Most code changes in “models.py” require you to create new migration schemas through “makemigrations.” This ensures that changes are tracked over time and your data stays intact.

Finally, you need to run “migrate” to apply these changes to the database. While “makemigrations” creates schema files in the “migrations/” folder, “migrate” applies those changes to the database.
___

Now go to your website and reload. 

Tip: if you closed the Browser and need the URL again, simply click on the "eye" icon again on your "Local Server". 

Open the toolbar by appending “/?edit” to the URL and “Log in as…” your user.

Click on “Page” and you will see a new entry called “Extra settings” – the one we added just a few moments ago. Click on the menu entry and add a subtitle and example image of your choice.

Before we can see the result of our new app, we have to adapt the Django templates for it. 

Head back to your editor and open “/templates/base.html.” Replace the first line:

{% load staticfiles i18n cms_tags sekizai_tags menu_tags %}

with the following code:

{% load staticfiles i18n cms_tags sekizai_tags menu_tags thumbnail %}

Next, search for:

{% placeholder header or %}
    <!-- Set your background image for this header on the line below. -->
    <header class="intro-header" style="background-image: url({% static 'img/about-bg.jpg' %})">
        <div class="container">
            <div class="row">
                <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
                    <div class="page-heading">
                        <h1>{% page_attribute "page_title" %}</h1>
                    </div>
                </div>
            </div>
        </div>
    </header>
{% endplaceholder %}

and replace the code with:

<!-- Set your background image for this header on the line below. -->
{% render_model_block request.current_page.pagefieldextension %}
<header class="intro-header" style="background-image: url(
    {% with image=request.current_page.pagefieldextension.background_image %}
        {% if image %}
            {% thumbnail image 1200x400 crop subject_location=image.subject_location %}
        {% else %}
            {% static 'img/about-bg.jpg' %}
        {% endif %}
    {% endwith %}
)">
    <div class="container">
        <div class="row">
            <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
                <div class="page-heading">
                    <h1>{% render_model request.current_page "page_title" %}</h1>
                    {% if request.current_page.pagefieldextension.subtitle %}
                        <hr class="small">
                        <span class="subheading">
                            {{ request.current_page.pagefieldextension.subtitle }}
                        </span>
                    {% endif %}
                </div>
            </div>
        </div>
    </div>
</header>
{% endrender_model_block %}

After saving these changes, reload the page again and you should see now the background image and subtitle you defined before.

If you want to change the default page title, head to “Page” followed by “Page settings” and fill in “Page title” with your desired title. This information will be different from what is displayed in the menu or page tree and serves specifically to set our heading title. 

Publish changes” to wrap things up.

You now have a working header in your local example. Let’s publish to the live site. For this, simply run:

git add -A && git commit -am "added page extension" && git push

followed by:

divio project push db && divio project push media

Type in “y” and hit enter when prompted to proceed.

___

Version Control
Divio Cloud is tracking all changes using Git, a widely used source code management system for software development. It’s a distributed revision control system with an emphasis on speed, data integrity and support for distributed, non-linear workflows.
___

Head back to the Divio Control Panel, select your site and choose “Deploy” on the "Test Server" for the changes to take effect. Once the deployment has concluded, open the site.

You’ve now successfully pushed your local changes to the cloud using the Divio Shell, including database, media and static files. This enables you to work locally, make all required changes and publish to the cloud once you are ready.

Did this answer your question?