Extending the page & title models¶
New in version 3.0.
You can extend the page and title models with your own fields (e.g. adding
an icon for every page) by using the extension models:
cms.extensions.PageExtension
and cms.extensions.TitleExtension
,
respectively.
What’s the difference?¶
The difference between a page extension and a title extension is related to the difference between the Page and Title models. Titles support pages by providing a storage mechanism, among other things, for language-specific properties of Pages. So, if you find that you need to extend the page model in a language-specific manner, for example, if you need to create language- specific keywords for each language of your pages, then you may need to use a TitleExtension. If the extension you’d like to create is the same for all of the different languages of the page, then you may be fine using a PageExtension.
How To¶
To add a field to the page model, create a class that inherits from
cms.extensions.PageExtension
. Make sure to import the
cms.extensions.PageExtension
model. Your class should live in one of your
apps’ models.py
(or module). Since PageExtension
(and
TitleExtension
) inherit from django.db.models.Model
, you are free to
add any field you want but make sure you don’t use a unique constraint on any
of your added fields because uniqueness prevents the copy mechanism of the
extension from working correctly. This means that you can’t use one-to-one
relations on the extension model. Finally, you’ll need to register the model
with using extension_pool
.
Here’s a simple example which adds an icon
field to the page:
from django.db import models
from cms.extensions import PageExtension
from cms.extensions.extension_pool import extension_pool
class IconExtension(PageExtension):
image = models.ImageField(upload_to='icons')
extension_pool.register(IconExtension)
Hooking the extension to the admin site¶
To make your extension editable, you must first create an admin class that
subclasses cms.extensions.PageExtensionAdmin
. This admin handles page
permissions. If you want to use your own admin class, make sure to exclude the
live versions of the extensions by using
filter(extended_page__publisher_is_draft=True)
on the queryset.
Continuing with the example model above, here’s a simple corresponding
PageExtensionAdmin
class:
from django.contrib import admin
from cms.extensions import PageExtensionAdmin
from .models import IconExtension
class IconExtensionAdmin(PageExtensionAdmin):
pass
admin.site.register(IconExtension, IconExtensionAdmin)
Since PageExtensionAdmin inherits from ModelAdmin, you’ll be able to use the normal set of Django ModelAdmin properties, as appropriate to your circumstance.
Once you’ve registered your admin class, a new model will appear in the top- level admin list.
Note that the field that holds the relationship between the extension and a CMS Page is non-editable, so it will not appear in the admin views. This, unfortunately, leaves the operator without a means of “attaching” the page extension to the correct pages. The way to address this is to use a CMSToolbar. Note that creating the admin hook is still required, because it creates the add and change admin forms that are required for the next step.
Using extensions in templates¶
To access a page extension in page templates you can simply access the approriate related_name field that is now available on the Page object.
As per the normal related_name naming mechanism, the appropriate field to
access is the same as your PageExtension model name, but lowercased. Assuming
your Page Extension model class is IconExtension
, the relationship to the
page extension model will be available on page.iconextension
. From there
you can access the extra fields you defined in your extension, so you can use
something like:
{% load staticfiles %}
{# rest of template omitted ... #}
{% if request.current_page.iconextension %}
<img src="{% static request.current_page.iconextension.image.url %}">
{% endif %}
Where request.current_page
is the normal way to access the current page
that is rendering the template.
It is important to remember that unless the operator has already assigned a
page extension to every page, a page may not have the iconextension
relationship available, hence the use of the {% if ... %}...{% endif %}
above.
Handling relations¶
If your PageExtension or TitleExtension includes a ForeignKey from another
model or includes a ManyToMany field, you should also override the method
copy_relations(self, oldinstance, language)
so that these fields are
copied appropriately when the CMS makes a copy of your extension to support
versioning, etc.
Here’s an example that uses a ManyToMany` field:
from django.db import models
from cms.extensions import PageExtension
from cms.extensions.extension_pool import extension_pool
class MyPageExtension(PageExtension):
page_categories = models.ManyToMany('categories.Category', blank=True, null=True)
def copy_relations(self, oldinstance, language):
for page_category in oldinstance.page_categories.all():
page_category.pk = None
page_category.mypageextension = self
page_category.save()
extension_pool.register(MyPageExtension)
Simplified Toolbar API¶
The simplified Toolbar API works by deriving your toolbar class from ExtensionToolbar
which provides the following API:
cms.extensions.toolbar.ExtensionToolbar._setup_extension_toolbar()
: this must be called first to setup the environment and do the permission checking;cms.extensions.toolbar.ExtensionToolbar.get_page_extension_admin()
: for page extensions, retrieves the correct admin url for the related toolbar item; returns the extension instance (or None if not exists) and the admin url for the toolbar item;cms.extensions.toolbar.ExtensionToolbar.get_title_extension_admin()
: for title extensions, retrieves the correct admin url for the related toolbar item; returns a list of the extension instances (or None if not exists) and the admin urls for each title of the current page;