Django CMS
django-cms is a lean enterprise content management system built on Django. It provides an intuitive drag-and-drop interface for managing pages and content, extensive plugin architecture, and multilingual support. The current stable version is 5.0.6, with regular patch releases and significant new major versions (like 4.x and 5.x) introducing substantial changes and requiring careful upgrade paths.
Common errors
-
TemplateSyntaxError: 'cms_tags' is not a registered tag library
cause The `cms_tags` template library was not loaded in your Django template.fixAdd `{% load cms_tags %}` at the top of any template file that uses Django CMS template tags (e.g., `{% placeholder %}`, `{% cms_toolbar %}`). -
NoReverseMatch at / 'cms_page_url' is not a registered namespace
cause Django CMS URLs are not correctly included in your project's `urls.py`, or they are placed incorrectly.fixEnsure you have `path('', include('cms.urls'))` in your project's `urls.py`, typically as the *last* pattern in your `urlpatterns` list to catch all CMS pages. -
AttributeError: 'Page' object has no attribute 'get_title'
cause This error often occurs when upgrading from older Django CMS versions (pre-4.0) where direct access to `page.get_title()` was common. The `Page` model's API has evolved.fixUse template tags like `{% page_attribute 'page_title' %}` or access title through `page.title` (if language is active) or `page.get_title_obj()` for language-aware operations in Python. -
OperationalError: no such table: cms_page
cause The database migrations for Django CMS (and possibly its dependencies) have not been applied.fixRun `python manage.py migrate` after adding `django-cms` and its required apps to `INSTALLED_APPS`.
Warnings
- breaking Upgrading from Django CMS 3.x or 4.x to 5.x introduces significant breaking changes. These include refactored template tags, changes to Apphook and plugin registration, and updates to the internal API and JavaScript frontend. A full upgrade guide must be consulted.
- breaking The template tag syntax has been significantly updated in Django CMS 5.x. Old usages of `{% placeholder 'name' %}` or `{% render_placeholder 'name' page %}` might need to be adjusted, especially for custom rendering or within `with` blocks.
- gotcha Incorrect configuration of `STATIC_URL`, `STATIC_ROOT`, `MEDIA_URL`, or `MEDIA_ROOT` can lead to missing static assets (CSS/JS) in the Django CMS admin toolbar or broken image/file uploads in the frontend.
- gotcha The order of middleware in `settings.py` is crucial for Django CMS. Incorrect ordering can lead to the toolbar not appearing, apphooks not resolving, or pages not rendering correctly.
Install
-
pip install django-cms==5.0.6 djangocms-text-ckeditor djangocms-picture
Imports
- Page
from cms.models import Page
- CMSPluginBase
from cms.plugin_base import CMSPluginBase
- apphook_pool
from cms.apphook_pool import apphook_pool
- toolbar_pool
from cms.toolbar_pool import toolbar_pool
- PlaceholderField
from cms.models.fields import PlaceholderField
Quickstart
# settings.py
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
INSTALLED_APPS = [
'djangocms_admin_style', # Must be before django.contrib.admin
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites', # Required by CMS
'cms', # Must be before 'menus'
'menus', # Must be before 'sekizai'
'sekizai', # Required for JS/CSS resource management
'treebeard', # Required by CMS for page trees
'djangocms_text_ckeditor', # Example plugin
# Add your project apps here
]
SITE_ID = 1
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.locale.LocaleMiddleware',
'cms.middleware.utils.ApphookReloadMiddleware',
'cms.middleware.toolbar.ToolbarMiddleware',
'cms.middleware.page.PageMiddleware',
]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'sekizai.context_processors.sekizai',
'cms.context_processors.cms_settings',
],
},
},
]
DEBUG = True
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'insecure-dev-key-please-change-this')
ALLOWED_HOSTS = ['*']
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
STATIC_URL = '/static/'
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
# urls.py
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('cms.urls')), # This must be the last URL pattern
]
# templates/base.html
# (create this file in your project's 'templates' directory)
{% load cms_tags sekizai_tags menu_tags %}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{% page_attribute "page_title" default_value="My Site" %}</title>
{% render_block "css" %}
</head>
<body>
{% cms_toolbar %}
<header>
<h1><a href="/">My Django CMS Site</a></h1>
{% show_menu 0 100 100 100 %}
</header>
<main>
{% placeholder "content" %}
</main>
<footer>
{% render_block "js" %}
</footer>
</body>
</html>