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_active,
                )

___

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:

<!-- Page Header -->
{% placeholder header or %}
<header class="masthead" style="background-image: url('{% static 'img/about-bg.jpg' %}')">
  <div class="overlay"></div>
  <div class="container">
    <div class="row">
      <div class="col-lg-8 col-md-10 mx-auto">
        <div class="page-heading">
          <h1>{% page_attribute "page_title" %}</h1>
        </div>
      </div>
    </div>
  </div>
</header>
{% endplaceholder %}

and replace the code with:

<!-- Page Header -->
{% render_model_block request.current_page.pagefieldextension %}
<header class="masthead" 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="overlay"></div>
    <div class="container">
      <div class="row">
        <div class="col-lg-8 col-md-10 mx-auto">
          <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 test site using the command line. 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.

To deploy from the command line, simply run:

divio project deploy

___

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

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?