
A recipe for adding a Django flatpages application with context change support, i18n and a rich content editor
Here is a short guide on converting a django application into a kind of cms editor that supports internalization and advanced editing. Perhaps it will be necessary only for myself, so as not to forget in the future.
Of course, it is absolutely possible to turn your django application into something cms-like, similar to a cms editor, using the stock django flatpages app.
But it’s not very convenient for the end user if you use it «as is».
So, we’ll try to do the following:
- Add flatpages app.
- Add django-ckeditor-5.
- Extend the data model to support internationalization of the cms database via django-translated-fields.
- Override some code fragments to allow passing the context to a flatpage renderer (as it isn’t supported byu default at the moment, for some reason).
So, in general, we can follow the instructions in the flatpages app manual, with some differencies and additions.
As an example, I will consider my recent django project, The March Cat Tales.
The result
Finally, you’ll have got a neat and user-friendly user interface like this:


Adding the flatpages app and the ckeditor extension
Add flatpages and ckeditor as described in their manual.
So, you’ll have to add smth like this into your settings.py
(you can take a look at mine for reference):
INSTALLED_APPS = [
...
'django.contrib.flatpages',
'django_ckeditor_5',
...
MIDDLEWARE = [
...
# Instead of stock 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware', see below
'tales_django.entities.flatpages.middleware.FlatpageFallbackMiddleware',
]
...
# Pointing to the context getter, see below
FLATPAGE_CONTEXT_GETTER = 'tales_django.get_flatpages_context.get_flatpages_context'
# Default flatpage template
FLATPAGE_DEFAULT_TEMPLATE = 'tales_django/flatpage.html.django'
...
# File storage class, see below
CKEDITOR_5_FILE_STORAGE = 'tales_django.ckeditor_storage.ckeditor_storage'
# Permissions, possible values are: 'staff', 'authenticated', 'any'
CKEDITOR_5_FILE_UPLOAD_PERMISSION = 'staff'
# Allow to dynamically change ckeditor locale
CKEDITOR_5_USER_LANGUAGE = True
CKEDITOR_5_CONFIGS = {
'default': {
# Set your toolbars and other stuff here
(Also ensure that django.contrib.sites
is included and SITE_ID
has been set up.)
ckeditor_storage provides only a folder path to store uploaded files.
Keep in mind that it’s impossible to use server browser features in a free version of ckeditor v.5 as all of therequired plugins (ckbox or ckfinder) aren’t be included in the build, because they’re a part of premium features. See the section «Includes the following ckeditor5 plugins…» in the django-ckeditor-5 quick reference.
See the «Using file managers» section in the CKEditor Ecosystem Documentation: «CKBox and CKFinder are both premium features. Contact us to receive an offer tailored to your needs.»
But if you have a license key, you can create your own configuration and use it in a js-based initalization. See the «Creating a CKEditor5 instance from javascript» section in the django-ckeditor-5 reference.
Extending the FlatPage data model
We’re going to add a tales_django/entities/App/models/FlatPage.py in order to add translated title and content fields:
class FlatPage(BaseFlatPage):
class Meta:
db_table = 'tales_django_flatpage'
verbose_name = _('Flat page')
verbose_name_plural = _('Flat pages')
page_title = TranslatedField(models.CharField(_('Page title'), max_length=200, default=''))
page_content = TranslatedField(CKEditor5Field(_('Page content'), blank=True, default=''))
updated_at = models.DateField(verbose_name=_('Updated at'), auto_now=True)
We use different fields names (those ones will be automatically translated by django-translated-fields) and a db name, and also added a field updated_at
to provide time stamps for sitemap generation.
Of course, it’s possible to add any other required fields here.
Also added admin model FlatPageAdmin.py and form FlatPageForm.py.
Overriding stock flatpages components
We’ll use our own implementation of flatpages’ views.py module to allow passing a FLATPAGE_CONTEXT_GETTER
parameters form the application settings.
Also we’ll have to use our own versions of middleware.py
and urls.py
to support these changes.
These files will be located in the tales_django/entities/flatpages folder.
The middleware.py module will contain almost exact copy of the stock FlatpageFallbackMiddleware
to process page rendering queue and process flat pages. Also, the urls.py will point the router to our own flatpage
view, contained
in the views.py.
The main change – is a get_context
function, which dynamically retrieves additional context data for flatpage templates:
def get_context(request: HttpRequest, flatpage: FlatPage):
...
This function provides a flexible way to add dynamic context data to flatpage templates without modifying the core flatpage rendering logic. It works by:
- Reading the
FLATPAGE_CONTEXT_GETTER
setting, which should be a string path to a callable that generates context data. - Dynamically importing and calling that function with the current
request
andflatpage
objects. - Returning the resulting context dictionary to be merged with the base context in the
render_flatpage
function.
Parameters:
request (HttpRequest)
: The current HTTP request object.flatpage (FlatPage)
: The flatpage object being rendered.
Two additional settings’ parameters are introduced:
-
FLATPAGE_CONTEXT_GETTER
: An optional string path to a callable that provides additional context for flatpage templates. It follows Django’s pattern of dynamic imports for configuration. The function should accept the current request and flatpage objects (request: HttpRequest, flatpage: FlatPage
) as parameters and return a context dictionary. Example:tales_django.flatpages_context.get_flatpages_context
. -
FLATPAGE_DEFAULT_TEMPLATE
: A default template used to render flatpages when the FlatPage model does not specify a specific template for a page. This serves as a site-wide default template for all flatpages. The template should be located in the templates directory and follow Django’s template naming conventions. Example:app/flatpage.html
. Default value:flatpages/default.html
.
We’ll have to add FlatpageFallbackMiddleware
to the MIDDLEWARE
list in the settings.py
, as it was exposed above (our app name is tales_django
, for a record):
MIDDLEWARE = [
...
'tales_django.entities.flatpages.middleware.FlatpageFallbackMiddleware',
And next, we have to add the router entry for a url pattern entry (in tales_django/entities/App/app_urlpatterns.py
):
app_urlpatterns = [
...
path(r'/', include('tales_django.entities.flatpages.urls'), name='django.contrib.flatpages.views.flatpage'),
(Our flatpages will be served directly from the root of the site.)
Another minor additions
Also were added:
Pass a context to template
Use smth like this to pass your data to a template (see the usage below):
def get_flatpages_context(request: HttpRequest, flatpage: FlatPage):
return {'your_data': 'Ok'}
This module mentioned in a FLATPAGE_CONTEXT_GETTER
settings parameter as tales_django.flatpages_context.get_flatpages_context
Create a template for the flat pages
You can specify the template location in the settings parameter FLATPAGE_DEFAULT_TEMPLATE
or in your data model on a per-page basis.
To access the database properties you can use:
<h1 class="page-title">
{{ flatpage.flatpage.page_title }}
</h1>
<div class="data-example">
Your context data: {{ your_data }}
</div>
<div class="text-content">
{{ flatpage.flatpage.page_content|safe }}
</div>
This template set with a FLATPAGE_DEFAULT_TEMPLATE
settings parameter.
Sitemap support
You can use the following recipe to add your flatpages with timestamps to the sitemap:
from django.contrib.flatpages.sitemaps import FlatPageSitemap
class CustomFlatPageSitemap(FlatPageSitemap):
changefreq = 'weekly'
def lastmod(self, obj):
return obj.flatpage.updated_at
sitemaps = {
...
'flatpages': CustomFlatPageSitemap,
app_urlpatterns = [
...
path('sitemap.xml', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'),
...
See django sitemap framework reference for more details.
A PR into the Django project
I also created a pull request into the django project to allow passing a context and setting a default template with the stock django flatpage application.