1. Extending the CMS: Examples

From this part onwards, this tutorial assumes you have done the Django Tutorial and we will show you how to integrate that poll app into the django CMS. If a poll app is mentioned here, we mean the one you get when finishing the Django Tutorial.

We assume your main urls.py looks somewhat like this:

  1. from django.conf.urls.defaults import *
  2. from django.contrib import admin
  3. admin.autodiscover()
  4. urlpatterns = patterns('',
  5. (r'^admin/', include(admin.site.urls)),
  6. (r'^polls/', include('polls.urls')),
  7. (r'^', include('cms.urls')),
  8. )

1.1. My First Plugin

A Plugin is a small bit of content you can place on your pages.

1.1.1. The Model

For our polling app we would like to have a small poll plugin, that shows one poll and let’s the user vote.

In your poll application’s models.py add the following model:

  1. from cms.models import CMSPlugin
  2. class PollPlugin(CMSPlugin):
  3. poll = models.ForeignKey('polls.Poll', related_name='plugins')
  4. def __unicode__(self):
  5. return self.poll.question

Note

django CMS Plugins must inherit from cms.models.CMSPlugin (or a subclass thereof) and not django.db.models.Model.

Run syncdb to create the database tables for this model or see Using South with Django-CMS to see how to do it using South

1.1.2. The Plugin Class

Now create a file cms_plugins.py in the same folder your models.py is in, so following the Django Tutorial, your polls app folder should look like this now:

  1. polls/
  2. __init__.py
  3. cms_plugins.py
  4. models.py
  5. tests.py
  6. views.py

The plugin class is responsible to provide the django CMS with the necessary information to render your Plugin.

For our poll plugin, write following plugin class:

  1. from cms.plugin_base import CMSPluginBase
  2. from cms.plugin_pool import plugin_pool
  3. from polls.models import PollPlugin as PollPluginModel
  4. from django.utils.translation import ugettext as _
  5. class PollPlugin(CMSPluginBase):
  6. model = PollPluginModel # Model where data about this plugin is saved
  7. name = _("Poll Plugin") # Name of the plugin
  8. render_template = "polls/plugin.html" # template to render the plugin with
  9. def render(self, context, instance, placeholder):
  10. context.update({'instance':instance})
  11. return context
  12. plugin_pool.register_plugin(PollPlugin) # register the plugin

Note

All plugin classes must inherit from cms.plugin_base.CMSPluginBase and must register themselves with the cms.plugin_pool.plugin_pool.

1.1.3. The Template

You probably noticed the render_template attribute on that plugin class, for our plugin to work, that template must exist and is responsible for rendering the plugin.

The template could look like this:

  1. <h1>{{ poll.question }}</h1>
  2. <form action="{% url polls.views.vote poll.id %}" method="post">
  3. {% csrf_token %}
  4. {% for choice in poll.choice_set.all %}
  5. <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
  6. <label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br />
  7. {% endfor %}
  8. <input type="submit" value="Vote" />
  9. </form>

Note

We don’t show the errors here, because when submitting the form you’re taken off this page to the actual voting page.

1.2. My First App (apphook)

Right now, external apps are statically hooked into the main urls.py, that is not the preferred way in the django CMS. Ideally you attach your apps to CMS Pages.

For that purpose you write CMS Apps. That is just a small class telling the CMS how to include that app.

CMS Apps live in a file called cms_app.py, so go ahead and create that to make your polls app look like this:

  1. polls/
  2. __init__.py
  3. cms_app.py
  4. cms_plugins.py
  5. models.py
  6. tests.py
  7. views.py

In this file, write:

  1. from cms.app_base import CMSApp
  2. from cms.apphook_pool import apphook_pool
  3. from django.utils.translation import ugettext_lazy as _
  4. class PollsApp(CMSApp):
  5. name = _("Poll App") # give your app a name, this is required
  6. urls = ["polls.urls"] # link your app to url configuration(s)
  7. apphook_pool.register(PollsApp) # register your app

Now remove the inclusion of the polls urls in your main urls.py so it looks like this:

  1. from django.conf.urls.defaults import *
  2. from django.contrib import admin
  3. admin.autodiscover()
  4. urlpatterns = patterns('',
  5. (r'^admin/', include(admin.site.urls)),
  6. (r'^', include('cms.urls')),
  7. )

Now open your admin in your browser and edit a CMS Page. Open the ‘Advanced Settings’ tab and choose ‘Polls App’ for your ‘Application’.

apphooks

Now for those changes to take effect, unfortunately you will have to restart your server. So do that and now if you navigate to that CMS Page, you will see your polls application.

1.3. My First Menu

Now you might have noticed that the menu tree stops at the CMS Page you created in the last step, so let’s create a menu that shows a node for each poll you have active.

For this we need a file called menu.py, create it and check your polls app looks like this:

  1. polls/
  2. __init__.py
  3. cms_app.py
  4. cms_plugins.py
  5. menu.py
  6. models.py
  7. tests.py
  8. views.py

In your menu.py write:

  1. from cms.menu_bases import CMSAttachMenu
  2. from menus.base import Menu, NavigationNode
  3. from menus.menu_pool import menu_pool
  4. from django.core.urlresolvers import reverse
  5. from django.utils.translation import ugettext_lazy as _
  6. from polls.models import Poll
  7. class PollsMenu(CMSAttachMenu):
  8. name = _("Polls Menu") # give the menu a name, this is required.
  9. def get_nodes(self, request):
  10. """
  11. This method is used to build the menu tree.
  12. """
  13. nodes = []
  14. for poll in Poll.objects.all():
  15. # the menu tree consists of NavigationNode instances
  16. # Each NavigationNode takes a label as first argument, a URL as
  17. # second argument and a (for this tree) unique id as third
  18. # argument.
  19. node = NavigationNode(
  20. poll.question,
  21. reverse('polls.views.detail', args=(poll.pk,)),
  22. poll.pk
  23. )
  24. nodes.append(node)
  25. return nodes
  26. menu_pool.register_menu(PollsMenu) # register the menu.

Now this menu alone doesn’t do a whole lot yet, we have to attach it to the Apphook first.

So open your cms_apps.py and write:

  1. from cms.app_base import CMSApp
  2. from cms.apphook_pool import apphook_pool
  3. from polls.menu import PollsMenu
  4. from django.utils.translation import ugettext_lazy as _
  5. class PollsApp(CMSApp):
  6. name = _("Poll App")
  7. urls = ["polls.urls"]
  8. menu = [PollsMenu] # attach a CMSAttachMenu to this apphook.
  9. apphook_pool.register(PollsApp)