Welcome to django-fluent-pages’s documentation!¶
This module provides a page tree, where each node type can be a different model. This allows you to structure your site CMS tree as you see fit. For example:
- Build a tree of flat pages, with a WYSIWYG editor.
- Build a tree with widget-based pages, by integrating django-fluent-contents.
- Build a tree structure of RST pages, by defining a
RstPage
type. - Build a tree of a homepage, subsection, and article node, each with custom fields like professional CMSes have.
Each node type can have it’s own custom fields, attributes, URL patterns and rendering.
In case you’re building a custom CMS, this module might just be suited for you, since it provides the tree for you, without bothering with anything else. The actual page contents is defined via page type plugins. To get up and running quickly, consult the quick-start guide. The chapters below describe the configuration of each specific plugin in more detail.
Preview¶

Getting started¶
Quick start guide¶
Installing django-fluent-pages¶
Make sure you have the base packages installed:
pip install Django
pip install django-fluent-pages
This command installs the base dependencies. As you add more of the Bundled Page Type Plugins, additional packages may be required. This is explained in the documentation for each plugin.
Tip
For optional dependency management, it is strongly recommended that you run the application inside a virtualenv.
Starting a project¶
For a quick way to have everything configured at once, use our template:
mkdir example.com
django-admin.py startproject "myexample" "example.com" -e "py,rst,example,gitignore" --template="https://github.com/edoburu/django-project-template/archive/django-fluent.zip"
And install it’s packages:
mkvirtualenv example.com
pip install -r example.com/requirements.txt
Otherwise, continue with the instructions below:
Basic Settings¶
In your existing Django project, the following settings need to be added to settings.py
:
INSTALLED_APPS += (
'fluent_pages',
'mptt',
'parler',
'polymorphic',
'polymorphic_tree',
# And optionally add the desired page types with their dependencies:
# - flat pages
'fluent_pages.pagetypes.flatpage',
'django_wysiwyg',
# - redirect nodes
'fluent_pages.pagetypes.redirectnode',
'any_urlfield', # optional but recommended
)
The following applications are used here:
- The main
fluent_pages
package that you always need. - The main dependencies.
- A selection of page type plugins, and their dependencies.
Since some extra page types are used here, make sure their dependencies are installed:
pip install django-fluent-pages[flatpage,redirectnode]
Afterwards, you can setup the database:
./manage.py migrate # use 'syncdb' for Django 1.6 and below
Note
Each page type is optional. Only the fluent_pages
application is required, allowing to write custom models and plugins.
Since a layout with the flatpage and redirectnode page types provides a good introduction, these are added here.
Each plugin is easily swappable for other implementations, exactly because everything is optional! You can use a different page type, or invert new page types with custom fields. It makes the CMS configurable in the way that you see fit.
URL configuration¶
The following needs to be added to urls.py
:
urlpatterns += patterns('',
url(r'', include('fluent_pages.urls'))
)
See also
- To add sitemaps support, see the Sitemaps integration documentation about that.
- Multilingual support may also require changes, see Multilingual support.
Template structure¶
The page output is handled by templates. When creating large websites, you’ll typically have multiple page templates. That’s why it’s recommended to have a single base template for all pages. This can expose the SEO fields that are part of every HTML page. As starting point, the following structure is recommended:
templates/
base.html
pages/
base.html
default.html
...
Now, create a pages/base.html
template:
{% extends "base.html" %}
{% block full-title %}{% if page.meta_title %}{{ page.meta_title }}{% else %}{{ block.super }}{% endif %}{% endblock %}
{% block meta-keywords %}{{ page.meta_keywords }}{% endblock %}
{% block meta-description %}{{ page.meta_description }}{% endblock %}
{% block extrahead %}{{ block.super }}{% if page.meta_robots %}
<meta name="robots" content="{{ page.meta_robots }}" />
{% endif %}{% endblock %}
These blocks should appear in your base.html
template off course.
Your site base.html
template could look something like this:
{% load fluent_pages_tags %}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="keywords" content="{% block meta-keywords %}{% endblock %}" />
<meta name="description" content="{% block meta-description %}{% endblock %}" />
<title>{% block full-head-title %}{% block head-title %}Untitled{% endblock %} | My site{% endblock %}</title>
{% block extrahead %}{% endblock %}
</head>
<body>
<header>
{% render_menu %}
</header>
<section id="contents">
<div id="main">
<h1>{{ page.title }}</h1>
{% render_breadcrumb %}
{% block main %}{% endblock %}
</div>
</section>
</body>
</html>
This base template does the following:
- Expose the placeholders for SEO fields.
- Add a main menu using
{% render_menu %}
- Add a breadcrumb using
{% render_breadcrumb %}
Tip
Whether page.title
should be included is your own decision.
You can also let clients enter the <h1>
in the WYSIWYG page content,
and reserve page.title
for menu titles alone.
This works really well in practise.
Adding page content¶
This package is very flexible when it comes to choosing page content. There are several page type plugins available:
fluent_pages.pagetypes.flatpage
- a simple page with WYSIWYG text box.fluent_pages.pagetypes.fluentpage
- a page with flexible content blocks.- Other known page types, such as a FAQ index or Blog index page.
The tree can also contain other node types, e.g.:
fluent_pages.pagetypes.redirectnode
- a redirect.fluent_pages.pagetypes.text
- a plain text file, e.g. to add a humans.txt file.- or any custom page type you create.
See also
In this quick-start manual, we’ll discuss the most important options briefly below. See the Bundled Page Type Plugins for the full documentation about each page type.
Using the flatpage plugin¶
The Flat page page type displays a simple WYSIWYG text box. To use it, install the packages and desired plugins:
pip install django-fluent-pages[flatpage]
pip install django-tinymce
Tip
You can also use CKEditor, Redactor or an other WYSIWYG editor, but for convenience TinyMCE is used as example. See the documentation of the The flatpage page type for details.
Add the following settings:
INSTALLED_APPS += (
'fluent_pages.pagetypes.flatpage',
'django_wysiwyg',
'tinymce',
)
DJANGO_WYSIWYG_FLAVOR = "tinymce" # or "tinymce_advanced"
FLUENT_TEXT_CLEAN_HTML = True
FLUENT_TEXT_SANITIZE_HTML = True
Make sure the database tables are created:
./manage.py migrate
To render the output properly, create a fluent_pages/base.html
file
so the Flat page pages can map the block names to the ones you use in base.html
:
{% extends "pages/base.html" %}
{% block head-title %}{% block title %}{% endblock %}{% endblock %}
{% block main %}{% block content %}{% endblock %}{% endblock %}
Using the fluentpage plugin¶
The Fluent page page type can fill parts of the page with flexible content blocks. To use it, install the packages and desired plugins:
pip install django-fluent-pages[fluentpage]
pip install django-fluent-contents[text,code,markup]
Configure the settings:
INSTALLED_APPS += (
'fluent_pages',
'fluent_contents',
# Page types
'fluent_pages.pagetypes.fluentpage',
'fluent_pages.pagetypes.flatpage',
'fluent_pages.pagetypes.redirectnode',
# Several content plugins
'fluent_contents.plugins.text', # requires django-wysiwyg
'fluent_contents.plugins.code', # requires pygments
'fluent_contents.plugins.gist',
'fluent_contents.plugins.googledocsviewer',
'fluent_contents.plugins.iframe',
'fluent_contents.plugins.markup',
'fluent_contents.plugins.rawhtml',
)
FLUENT_MARKUP_LANGUAGE = 'reStructuredText' # can also be markdown or textile
Make sure the database tables are created:
./manage.py migrate
The template can be filled with the “placeholder” tags from django-fluent-contents:
{% extends "mysite/base.html" %}
{% load placeholder_tags %}
{% block main %}
<section id="main">
<article>
{% block pagetitle %}<h1 class="pagetitle">{{ page.title }}</h1>{% endblock %}
{% page_placeholder "main" role='m' %}
</article>
<aside>
{% page_placeholder "sidebar" role='s' %}
</aside>
</section>
{% endblock %}
Testing your new shiny project¶
Congrats! At this point you should have a working installation. Now you can just login to your admin site and see what changed.
Configuration¶
A quick overview of the available settings:
FLUENT_PAGES_BASE_TEMPLATE = "fluent_pages/base.html"
FLUENT_PAGES_TEMPLATE_DIR = TEMPLATE_DIRS[0]
FLUENT_PAGES_RELATIVE_TEMPLATE_DIR = True
FLUENT_PAGES_DEFAULT_IN_NAVIGATION = True
FLUENT_PAGES_KEY_CHOICES = ()
# Advanced
FLUENT_PAGES_PREFETCH_TRANSLATIONS = False
FLUENT_PAGES_FILTER_SITE_ID = True
FLUENT_PAGES_PARENT_ADMIN_MIXIN = None
FLUENT_PAGES_CHILD_ADMIN_MIXIN = None
ROBOTS_TXT_DISALLOW_ALL = DEBUG
Template locations¶
FLUENT_PAGES_BASE_TEMPLATE¶
The name of the base template. This setting can be overwritten to point all templates to another base template. This can be used for the Flat page page type.
FLUENT_PAGES_TEMPLATE_DIR¶
The template directory where the “Layouts” model can find templates.
By default, this is the first path in TEMPLATE_DIRS
. It can also be set explicitly, for example:
FLUENT_PAGES_TEMPLATE_DIR = os.path.join(SRC_DIR, 'frontend', 'templates')
FLUENT_PAGES_RELATIVE_TEMPLATE_DIR¶
Whether template paths are stored as absolute or relative paths. This defaults to relative paths:
FLUENT_PAGES_RELATIVE_TEMPLATE_DIR = True
Preferences for the admin¶
FLUENT_PAGES_KEY_CHOICES¶
Pages can be “tagged” to be easily found in the page tree. Example value:
FLUENT_PAGES_KEY_CHOICES = (
# Allow to tag some pages, so they can be easily found by templates.
('search', _("Search")),
('contact', _("Contact")),
('terms', _("Terms and Conditions")),
('faq', _("FAQ page")),
('impactmap', _("Impact map")),
)
When this value is defined, a “Page identifier” option appears in the “Publication settings” fieldset.
Pages which are marked with an identifier can be found
using Page.objects.get_for_key()
.
Performance optimizations¶
FLUENT_PAGES_PREFETCH_TRANSLATIONS¶
Enable this to prefetch all translations at a regular page. This is useful to display a language choice menu:
FLUENT_PAGES_PREFETCH_TRANSLATIONS = True
SEO settings¶
ROBOTS_TXT_DISALLOW_ALL¶
When using RobotsTxtView
, enable this setting for beta websites.
This makes sure such site won’t be indexed by search engines.
Off course, it’s recommended to add HTTP authentication to such site,
to prevent accessing the site at all.
Advanced admin settings¶
FLUENT_PAGES_FILTER_SITE_ID¶
By default, each Site
model has it’s own page tree.
This enables the multi-site support, where you can run multiple instances with different sites.
To run a single Django instance with multiple sites, use a module such as django-multisite.
You can disable it using this by using:
FLUENT_PAGES_FILTER_SITE_ID = False
FLUENT_PAGES_PARENT_ADMIN_MIXIN / FLUENT_PAGES_CHILD_ADMIN_MIXIN¶
By setting this value, this module will insert your class in the admin. This can be used to override methods, or provide integration other third party applications such as django-guardian.
- The “parent admin” handles the list display for pages.
- The “child admin” handles the edit and delete views for pages.
Example setting:
FLUENT_PAGES_PARENT_ADMIN_MIXIN = 'apps.auth_utils.page_admin.FluentPagesParentAdminMixin'
FLUENT_PAGES_CHILD_ADMIN_MIXIN = 'apps.auth_utils.page_admin.FluentPagesChildAdminMixin'
Your project needs to provide those classes, and can implement or override admin methods there.
Advanced language settings¶
The language settings are copied by default from the django-parler variables. If you have to provide special settings (basically fork the settings), you can provide the following values:
FLUENT_DEFAULT_LANGUAGE_CODE = PARLER_DEFAULT_LANGUAGE_CODE = LANGUAGE_CODE
FLUENT_PAGES_DEFAULT_LANGUAGE_CODE = FLUENT_DEFAULT_LANGUAGE_CODE
FLUENT_PAGES_LANGUAGES = PARLER_LANGUAGES
The template tags¶
The template tags provide a way to include a menu, or breadcrumb in the website. Load the tags using:
{% load fluent_pages_tags %}
The breadcrumb¶
The breadcrumb of the current page can be rendered using:
{% render_breadcrumb %}
It’s possible to render the breadcrumb using a custom template:
{% render_breadcrumb template="fluent_pages/parts/breadcrumb.html" %}
The breadcrumb template could look like:
{% if breadcrumb %}
<ul>
{% for item in breadcrumb %}
<li{% if forloop.last %} class="last"{% endif %}><a href="{{ item.url }}">{{ item.title }}</a></li>
{% endfor %}
</ul>
{% endif %}
Advanced features¶
Fetching ‘site’ and ‘page’ variables¶
The templates receive a site
and page
variable by default.
In case the template is rendered outside the regular loop, these fields can be fetched:
{% get_fluent_page_vars %}
Locating custom page type views¶
When a custom page type provides additional views, these can be fetched using:
{% load appurl_tags %}
{% appurl "my_viewname" %}
{% appurl "my_viewname" arg1 arg2 %}
{% appurl "my_viewname" kwarg1=value kwargs2=value %}
These tags locate the page in the page tree, and resolve the view URL from there.
Sitemaps integration¶
The pages can be included in the sitemap that django.contrib.sitemaps
provides.
This makes it easier for search engines to index all pages.
Add the following in urls.py
:
from fluent_pages.sitemaps import PageSitemap
from fluent_pages.views import RobotsTxtView
sitemaps = {
'pages': PageSitemap,
}
urlpatterns += patterns('',
url(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}),
url(r'^robots.txt$', RobotsTxtView.as_view()),
)
The django.contrib.sitemaps
should be included in the INSTALLED_APPS
off course:
INSTALLED_APPS += (
'django.contrib.sitemaps',
)
The pages should now be visible in the sitemap.xml
.
A sitemap is referenced in the robots.txt
URL.
When using the bundled RobotsTxtView
in the example above, this happens by default.
The contents of the robots.txt
URL can be overwritten by overriding the robots.txt
template.
Note that the robots.txt
file should point to the sitemap with the full domain name included:
Sitemap: http://full-website-domain/sitemap.xml
For more details about the robots.txt
URL, see the documentation at
http://www.robotstxt.org/ and https://support.google.com/webmasters/answer/6062608?hl=en&rd=1
Note
When using Nginx, verify that robots.txt
is also forwarded to your Django application.
For example, when using location = /robots.txt { access_log off; log_not_found off; }
,
the request will not be forwarded to Django because this replaces the standard location / { .. }
block.
Multilingual support¶
This package supports creating content in multiple languages. This feature is based on django-parler. Historical anecdote: django-parler was created to make this CMS multilingual.
The enable multiple languages, configuring django-parler is sufficient.
Configuration¶
LANGUAGES = (
('en', _("Global Site")),
('en-us', _("US Site")),
('it', _('Italian')),
('nl', _('Dutch')),
('fr', _('French')),
('es', _('Spanish')),
)
PARLER_DEFAULT_LANGUAGE_CODE = 'en' # defaults to LANGUAGE_CODE
SITE_ID = None
PARLER_LANGUAGES = {
None: (
# Default SITE_ID, all languages
{'code': lang[0]} for lang in LANGUAGES
),
2: (
# SITE_ID 2: only english/french
{'code': 'en',}
{'code': 'fr',}
),
'default': {
# This is applied to each entry in this setting:
'hide_untranslated': False,
'hide_untranslated_menu_items': False,
# 'fallback': 'en' # set by PARLER_DEFAULT_LANGUAGE_CODE
}
}
There are two extra values that can be used:
hide_untranslated
: if set toTrue
, untranslated pages are not accessible.hide_untranslated_menu_items
: is set toTrue
, untranslated pages are not visible in the menu.
These values can be used in the “default” section, or in each dictionary entry per site.
Accessing content¶
There are several ways to expose translated content.
One way is adding a subpath in the URL by using i18n_patterns()
:
Using i18n_patterns¶
Add the following to settings.py
:
MIDDLEWARE_CLASSES += (
'django.middleware.locale.LocaleMiddleware', # or your own override/replacement
)
Add to urls.py
:
from django.conf.urls import patterns, url
from django.conf.urls.i18n import i18n_patterns
from django.contrib import admin
from fluent_pages.sitemaps import PageSitemap
sitemaps = {
# Place sitemaps here
'pages': PageSitemap,
}
admin.autodiscover()
urlpatterns = patterns('',
# All URLs that should not be prefixed with the country code,
# e.g. robots.txt or django admin.
) + i18n_patterns('',
# All URLS inside the i18n_patterns() get prefixed with the country code:
# Django admin
url(r'^admin/', admin.site.urls),
# SEO API's per language
url(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}),
# CMS modules
url(r'', include('fluent_pages.urls')),
)
Using custom middleware¶
Nothing prevents you from writing custom middleware that sets the frontend language. For example:
Add the following to settings.py
:
LANGUAGE_CODE = 'en' # default, e.g. for the admin
FRONTEND_LANGUAGE_CODE = 'de'
MIDDLEWARE_CLASSES += (
'mysite.middleware.FrontendLanguageMiddleware',
)
The custom middleware code:
from django.conf import settings
from django.utils import translation
from django.urls import reverse_lazy
class FrontendLanguageMiddleware(object):
"""
Change the active language when visiting a frontend page.
"""
def __init__(self):
# NOTE: not locale aware, assuming the admin stays at a single URL.
self._admin_prefix = reverse_lazy('admin:index', prefix='/')
def process_request(self, request):
if request.path_info.startswith(str(self._admin_prefix)):
return # Excluding the admin
if settings.FRONTEND_LANGUAGE_CODE != settings.LANGUAGE_CODE:
translation.activate(settings.FRONTEND_LANGUAGE_CODE)
This could even include detecting the sub-domain, and setting the language accordingly.
All queries that run afterwards read the active language setting, and display the content is the given language.
You can take this further and make Django aware of the sub-domain in it’s URLs by
overriding ABSOLUTE_URL_OVERRIDES
in the settings.
The Page
provides a default_url
attribute for this specific use-case.
You’ll also have to override the sitemap, as it won’t take absolute URLs into account.
Management Commands¶
The following management commands are provided for administrative utilities:
make_language_redirects¶
When a language is unmaintained at the site, use this command to generate the URL redirects. The command outputs a script for the web server (currently only in Nginx format).
Options:
--from=language
: the old language--to=language
: the new language--format=nginx
: the format--site=id
: the site for which redirects are created.
Example:
python manage.py make_language_redirects --from=it --to=en --format=nginx --site=1
rebuild_page_tree¶
In the unlikely event that the page tree is broken, this utility repairs the tree. This happened in earlier releases (before 1.0) when entire trees were moved in multi-lingual sites.
It regenerates the MPTT fields and URLs.
Options:
-p
/--dry-run
: tell what would happen, but don’t make any changes.-m
/--mptt-only
: only regenerate the MPTT fields, not the URLs of the tree.
Example:
python manage.py rebuild_page_tree
remove_stale_pages¶
New in version 1.1.2.
In the unlikely event that a page type was removed, but it’s page nodes still exist, this command can be used to repair the tree. It removes the old pages that point to content types that no longer exist.
Options:
-p
/--dry-run
: tell what would happen, but don’t make any changes.
Example:
python manage.py remove_stale_pages --dry-run
Using the page type plugins¶
Bundled Page Type Plugins¶
This module ships has a set of plugins bundled by default, as they are useful for a broad range of web sites. The plugin code also serves as an example and inspiration to create your own modules, so feel free browse the source code of them.
The available plugins are:
The flatpage page type¶
The flatpage provides a simple page type with a WYSIWYG (“What You See is What You Get”) editor.
The WYSIWYG editor is provided by django-wysiwyg, making it possible to switch to any WYSIWYG editor of your choice.
Note
This page type may seem a bit too simply for your needs. However, in case additional fields are needed, feel free to create a different page type yourself. This page type can serve as canonical example.
Installation¶
Install the dependencies via pip:
pip install django-fluent-pages[flatpage]
This installs the django-wysiwyg package.
Add the following settings to settings.py
:
INSTALLED_APPS += (
'fluent_pages.pagetypes.flatpage',
'django_wysiwyg',
)
Using CKEditor¶
To use CKEditor, install django-ckeditor:
pip install django-ckeditor
Add the following settings to settings.py
:
INSTALLED_APPS += (
'ckeditor',
)
DJANGO_WYSIWYG_FLAVOR = "ckeditor"
Using TinyMCE¶
To use TinyMCE, install django-tinymce:
pip install django-tinymce
Add the following settings to settings.py
:
INSTALLED_APPS += (
'tinymce',
)
DJANGO_WYSIWYG_FLAVOR = "tinymce" # or "tinymce_advanced"
Using Redactor¶
To use Redactor, tell django-wysiwyg where to find the static files. This is done on purpose to respect the commercial license.
DJANGO_WYSIWYG_FLAVOR = "redactor"
DJANGO_WYSIWYG_MEDIA_URL = "/static/vendor/imperavi/redactor/"
Template layout¶
To integrate the output of the page into your website design, overwrite fluent_pages/base.html
.
The following blocks have to be mapped to your website theme base template:
- title: the sub title to display in the
<title>
tag. - content: the content to display in the
<body>
tag. - meta-description - the
value
of the meta-description tag. - meta-keywords - the
value
for the meta-keywords tag.
In case your website base template uses different names for those blocks, create a fluent_pages/base.html
file to map the names:
{% extends "pages/base.html" %}
{% block head-title %}{% block title %}{% endblock %}{% endblock %}
{% block main %}{% block content %}{% endblock %}{% endblock %}
Further output tuning¶
The name of the base template can also be changed using the FLUENT_PAGES_BASE_TEMPLATE setting.
The page type itself is rendered using fluent_pages/pagetypes/flatpage/default.html
,
which extends the fluent_pages/base.html
template.
Configuration settings¶
The following settings are available:
DJANGO_WYSIWYG_FLAVOR = "yui_advanced"
FLUENT_TEXT_CLEAN_HTML = True
FLUENT_TEXT_SANITIZE_HTML = True
DJANGO_WYSIWYG_FLAVOR¶
The DJANGO_WYSIWYG_FLAVOR
setting defines which WYSIWYG editor will be used.
As of django-wysiwyg 0.5.1, the following editors are available:
- ckeditor - The CKEditor, formally known as FCKEditor.
- redactor - The Redactor editor (requires a license).
- tinymce - The TinyMCE editor, in simple mode.
- tinymce_advanced - The TinyMCE editor with many more toolbar buttons.
- yui - The YAHOO editor (the default)
- yui_advanced - The YAHOO editor with more toolbar buttons.
Additional editors can be easily added, as the setting refers to a set of templates names:
- django_wysiwyg/flavor/includes.html
- django_wysiwyg/flavor/editor_instance.html
For more information, see the documentation of django-wysiwyg about extending django-wysiwyg.
FLUENT_TEXT_CLEAN_HTML¶
If True
, the HTML tags will be rewritten to be well-formed.
This happens using either one of the following packages:
The fluentpage page type¶
The fluentpage provides a page type where parts of the page can be filled with flexible content blocks.
This feature is provided by django-fluent-contents.
The combination of django-fluent-pages and django-fluent-contents provides the most flexible page layout. It’s possible to use a mix of standard plugins (e.g. text, code, forms) and customly defined plugins to facilitate complex website designs. See the documentation of django-fluent-contents for more details.
Installation¶
Install the dependencies via pip:
pip install django-fluent-pages[fluentpage]
pip install django-fluent-contents[text]
This installs the django-fluent-contents package.
Add the following settings to settings.py
:
INSTALLED_APPS += (
'fluent_pages.pagetypes.fluentpage',
'fluent_contents',
# The desired plugins for django-fluent-contents, e.g:
'fluent_contents.plugins.text',
'django_wysiwyg',
)
Template design¶
To render the page, include the tags that django-fluent-contents uses to define placeholders. For example:
{% extends "mysite/base.html" %}
{% load placeholder_tags %}
{% block main %}
<section id="main">
<article>
{% block pagetitle %}<h1 class="pagetitle">{{ page.title }}</h1>{% endblock %}
{% page_placeholder "main" role='m' %}
</article>
<aside>
{% page_placeholder "sidebar" role='s' %}
</aside>
</section>
{% endblock %}
These placeholders will be detected and displayed in the admin pages.
Place the template in the template folder that FLUENT_PAGES_TEMPLATE_DIR points to.
By default, that is the first path in TEMPLATE_DIRS
.
Configuration¶
The page type itself doesn’t provide any configuration options, everything can be fully configured by configuring django-fluent-contents. See the documentation of each of these bundled content plugins to use them:
- The code plugin
- The commentsarea plugin
- The disquscommentsarea plugin
- The formdesignerlink plugin
- The gist plugin
- The googledocsviewer plugin
- The iframe plugin
- The markup plugin
- The oembeditem plugin
- The rawhtml plugin
- The sharedcontent plugin
- The text plugin
- The twitterfeed plugin
Creating new plugins¶
A website with custom design elements can be easily editable by creating custom plugins.
Creating new plugins is not complicated at all, and simple plugins can can easily be created within 15 minutes.
The documentation of django-fluent-contents explains how to create new plugins in depth.
Advanced features¶
This module also provides the FluentPageBase
and FluentPageAdmin
classes,
which can be used as base classes for custom page types
that also use the same layout mechanisms.
The redirectnode page type¶
The redirectnode allows adding a URL path that redirects the website visitor.
Installation¶
Install the dependencies via pip:
pip install django-fluent-pages[redirectnode]
This installs the django-any-urlfield package.
Add the following settings to settings.py
:
INSTALLED_APPS += (
'fluent_pages.pagetypes.redirectnode',
'any_urlfield',
)
Configuration¶
This page type works out of the box.
By default, the admin can choose between an “External URL” and “Page”.
Other models can also be included too, as long as they have a get_absolute_url
method.
Register the respective models to django-any-urlfield:
from any_urlfield.models import AnyUrlField
AnyUrlField.register_model(Article)
See the any_urlfield.models
documentation for details.
The textfile page type¶
The textfile allows adding a URL node that displays plain text.
This page type serves as simple demo, and can also be used to add a
custom robots.txt
, humans.txt file or README
file to the page tree.
Note
Currently, it’s still required to use the “Override URL” field in the form to include a file extension, as the “Slug” field does not allow this.
Installation¶
Add the following settings to settings.py
:
INSTALLED_APPS += (
'fluent_pages.pagetypes.textfile',
)
Other known page types¶
Blog page¶
The django-fluent-blogs module provides a “Blog page” type, which can be used to include a “Blog” in the page tree.
To integrate it with this module, configure it using:
INSTALLED_APPS += (
'fluent_blogs',
'fluent_blogs.pagetypes.blogpage',
)
See the documentation of django-fluent-blogs for details.
FAQ page¶
The django-fluent-faq module provides a “FAQ page” type, which displays a list of FAQ questions and categories.
To integrate it with this module, configure it using:
INSTALLED_APPS += (
'fluent_faq',
'fluent_faq.pagetypes.faqpage',
)
See the documentation of django-fluent-faq for details.
Open ideas¶
Other page types can also be written, for example:
- a “Portfolio” page type.
- a “Split test” page type.
- a “Flat page” with reStructuredText content.
- a “Web shop” page type.
- a “Subsite section” page type.
See the next chapter, Creating new page types to create such plugins.
Creating new page types¶
This module is specifically designed to easily add custom page types.
Typically, a project consists of some standard modules, and perhaps one or two custom types. Creating these is easy, as shown in the following sections:
Example plugin code¶
A plugin is a standard Django/Python package. As quick example, let’s create a webshop page.
The plugin can be created in your Django project, in a separate app
which can be named something like pagetypes.shoppage
or mysite.pagetypes
.
Example code¶
For the pagetypes.shoppage
package, the following files are needed:
__init__.py
, naturally.models.py
for the database model.page_type_plugins.py
for the plugin definition.
models.py¶
The models in models.py
needs to inherit from the Page
class,
the rest is just standard Django model code.
from django.db import models
from fluent_pages.models import Page
from myshop.models import ProductCategory
class ProductCategoryPage(Page):
product_category = models.ForeignKey(ProductCategory)
class Meta:
verbose_name = 'Product category page'
verbose_name_plural = 'Product category pages'
This Page
class provides the basic fields to integrate the model in the tree.
page_type_plugins.py¶
The page_type_plugins.py
file can contain multiple plugins, each should inherit from the PageTypePlugin
class.
from django.conf.urls import patterns, url
from fluent_pages.extensions import PageTypePlugin, page_type_pool
from .models import ProductCategoryPage
@page_type_pool.register
class ProductCategoryPagePlugin(PageTypePlugin):
""""
A new page type plugin that binds the rendering and model together.
"""
model = ProductCategoryPage
render_template = "products/productcategorypage.html"
# Custom URLs
urls = patterns('myshop.views',
url('^(?P<slug>[^/]+)/$', 'product_details'),
)
The plugin class binds all parts together; the model, metadata, and rendering code.
Either the get_response()
function can be overwritten,
or a render_template
can be defined.
The other fields, such as the urls
are optional.
productcategorypage.html¶
The default get_response()
code renders the page with a template.
This can be used to generate the HTML:
{% extends "pages/base.html" %}
{% block headtitle %}{{ page.title }}{% endblock %}
{% block main %}
<p>
Contents of the category: {{ page.product_category }} ({{ page.product_category.products.count }} products).
</p>
<div id="products">
....
</div>
{% endblock %}
Note how the page
variable is available, and the extra product_category
field can be accessed directly.
Wrapping up¶
The plugin is now ready to use.
Don’t forget to add the pagetypes.shoppage
package to the INSTALLED_APPS
, and create the tables:
./manage.py syncdb
Now, the plugin will be visible in the “Add page” dialog:

After adding it, the admin interface will be visible:

The appearance on the website depends on the site’s CSS theme, of course.
This example showed how a new plugin can be created within 5-15 minutes! To continue, see Customizing the frontend rendering to implement custom rendering.
Customizing the frontend rendering¶
As displayed in the Example plugin code page, a page type is made of two classes:
- A model class in
models.py
. - A plugin class in
page_type_plugins.py
.
The plugin class renders the model instance using:
- A custom
get_response()
method. - The
render_template
attribute,get_render_template()
method and optionallyget_context()
method.
Simply stated, a plugin provides the “view” of the “page”.
Simple rendering¶
To quickly create plugins with little to no effort, only the render_template
needs to be specified.
The template code receives the model object via the instance
variable.
To switch the template depending on the model, the get_render_template()
method
can be overwritten instead. For example:
@page_type.register
class MyPageType(PageTypePlugin):
# ...
def get_render_template(self, request, page, **kwargs):
return page.template_name or self.render_template
To add more context data, overwrite the get_context
method.
Custom rendering¶
Instead of only providing extra context data,
the whole get_response()
method can be overwritten as well.
The textfile and redirectnode page types use this for example:
def get_response(self, request, redirectnode, **kwargs):
response = HttpResponseRedirect(redirectnode.new_url)
response.status_code = redirectnode.redirect_type
return response
The standard get_response()
method basically does the following:
def get_response(self, request, page, **kwargs):
render_template = self.get_render_template(request, page, **kwargs)
context = self.get_context(request, page, **kwargs)
return self.response_class(
request = request,
template = render_template,
context = context,
)
- It takes the template from
get_render_template()
. - It uses the context provided by
get_context()
. - It uses
response_class()
class to output the response.
Note
The PageTypePlugin
class is instantiated once, just like the ModelAdmin
class.
Unlike the Django class based views, it’s not possible to store state at the local instance.
Customizing the admin interface¶
The admin rendering of a page type is fully customizable.
Changed in version 1.2: It’s no longer necessary to define the model_admin
attribute.
Registering the custom admin class instead using admin.site.register()
or the @admin.register()
decorator.
@page_type_pool.register
class ProductCategoryPagePlugin(PageTypePlugin):
""""
A new page type plugin that binds the rendering and model together.
"""
model = ProductCategoryPage
render_template = "products/productcategorypage.html"
model_admin = ProductCategoryPageAdmin # only required for fluent-pages 1.1 and below.
The admin class needs to inherit from one of the following classes:
fluent_pages.admin.PageAdmin
fluent_pages.admin.HtmlPageAdmin
- in case the model extends fromHtmlPage
fluent_pages.pagetypes.fluentpage.admin.FluentPageAdmin
- in case the model extends fromFluentPageBase
The admin can be used to customize the “add” and “edit” fields for example:
from django.contrib import admin
from fluent_pages.admin import PageAdmin
from .models import ProductCategoryPage
@admin.register(ProductCategoryPage)
class ProductCategoryPageAdmin(PageAdmin):
raw_id_fields = PageAdmin.raw_id_fields + ('product_category',)
Despire being registered in the admin, the model won’t show up in the index page.
The “list” page is never used, as this is rendered by the main PageAdmin
class.
Only the “add” and “edit” page are exposed by the PageAdmin
class too.
Customizing fieldsets¶
To deal with model inheritance, the fieldsets are not set in stone in the fieldsets
attribute.
Instead, the fieldsets are created dynamically using the the base_fieldsets
value as starting point.
Any unknown fields (e.g. added by derived models) will be added to a separate “Contents” fieldset.
The default layout of the PageAdmin
class is:
base_fieldsets = (
PageAdmin.FIELDSET_GENERAL,
PageAdmin.FIELDSET_MENU,
PageAdmin.FIELDSET_PUBLICATION,
)
The default layout of the HtmlPageAdmin
is:
base_fieldsets = (
HtmlPageAdmin.FIELDSET_GENERAL,
HtmlPageAdmin.FIELDSET_SEO,
HtmlPageAdmin.FIELDSET_MENU,
HtmlPageAdmin.FIELDSET_PUBLICATION,
)
The title of the custom “Contents” fieldset is configurable with the extra_fieldset_title
attribute.
Customizing the form¶
Similar to the base_fieldsets
attribute,
there is a base_form
attribute to use for the form.
Inherit from the PageAdminForm
class to create a custom form,
so all base functionality works.
Adding custom URLs¶
Page types can provide custom URL patterns. These URL patterns are relative to the place where the page is added to the page tree.
This feature is useful for example to:
- Have a “Shop” page type where all products are sub pages.
- Have a “Blog” page type where all articles are displayed below.
To use this feature, provide a URLconf or an inline patterns()
list in the page type plugin.
Basic example¶
To have a plugin with custom views, add the urls
attribute:
@page_type_pool.register
class ProductCategoryPagePlugin(PageTypePlugin):
# ...
urls = patterns('myshop.views',
url('^(?P<slug>[^/]+)/$', 'product_details'),
)
The view is just a plain Django view:
from django.http import HttpResponse
from django.shortcuts import get_object_or_404, render
from myshop.models import Product
def product_details(request, slug):
product = get_object_or_404(Product, slug=slug)
return render(request, 'products/product_details.html', {
'product': product
})
Other custom views can be created in the same way.
Resolving URLs¶
The URLs can’t be resolved using the standard reverse()
function unfortunately.
The main reason is that it caches results internally for the lifetime of the WSGI container,
meanwhile pages may be rearranged by the admin.
Hence, a app_reverse()
function is available.
It can be used to resolve the product page:
from fluent_pages.urlresolvers import app_reverse
app_reverse('product_details', kwargs={'slug': 'myproduct'})
In templates, there is an appurl
tag which accomplishes the same effect:
{% load appurl_tags %}
<a href="{% appurl 'product_details' slug='myproduct' %}">My Product</a>
See also
The example application in the source demonstrates this feature.
Compatibility with regular URLconf¶
An application can provide a standard urls.py
for regular Django support,
and still support page type URLs too. For this special case,
the mixed_reverse()
function is available.
It attemps to resolve the view in the standard URLconf first,
and falls back to app_reverse()
if the view is not found there.
A mixedurl
template tag has to be included in the application itself. Use the following code as example:
@register.tag
def mixedurl(parser, token):
if 'fluent_pages' in settings.INSTALLED_APPS:
from fluent_pages.templatetags.appurl_tags import appurl
return appurl(parser, token)
else:
from django.template.defaulttags import url
return url(parser, token)
See also
The django-fluent-blogs application uses this feature to optionally integrate the blog articles to the page tree.
Integration with fluent-contents¶
The bundled fluent page type provides a page type
where parts of the page can be filled with flexible content blocks.
This feature can be used in your custom page types as well.
The fluent_pages.integration.fluent_contents
package provides
all classes to make this integration painless.
Note
The support for those flexible blocks is provided by the stand-alone django-fluent-contents package, which is an optional dependency. Both fluent-pages and fluent-contents are stand-alone packages, which you can mix and match freely with other software.
Example case: donation page¶
In some pages, the user is guided through several steps. At each step, staff members have to be able to enter CMS page content.
This can be handled in a smart way by exposing all situations through a single page.
In this simple example, a “Donation Page” was created as custom page type. This allowed editing the opening view, and “thank you view” as 2 separate area’s.
models.py
¶
from fluent_pages.integration.fluent_contents.models import FluentContentsPage
class DonationPage(FluentContentsPage):
"""
It has a fixed template, which can be used to enter the contents for all wizard steps.
"""
class Meta:
verbose_name = _("Donation Page")
verbose_name_plural = _("Donation Pages")
admin.py
¶
from fluent_pages.integration.fluent_contents.admin import FluentContentsPageAdmin
class DonationPageAdmin(FluentContentsPageAdmin):
"""
Admin for "Donation Page" in the CMS.
"""
# This template is read to fetch the placeholder data, which displays the tabs.
placeholder_layout_template = 'pages/donation.html'
page_type_plugins.py
¶
from django.conf.urls import url
from fluent_pages.extensions import page_type_pool
from fluent_pages.integration.fluent_contents.page_type_plugins import FluentContentsPagePlugin
from .admin import DonationPageAdmin
from .models import DonationPage
from .views import DonationSuccessView
@page_type_pool.register
class DonationPagePlugin(FluentContentsPagePlugin):
"""
Custom page type for the donation page
This page type can be inserted somewhere within the page tree,
and all it's wizard sub-pages will be read from it.
"""
model_admin = DonationPageAdmin
model = DonationPage
urls = [
# root = donation starting page (handled as standard page)
url(r'^step1/', DonationStep1View.as_view(), name='donation-step1'),
url(r'^step2/', DonationStep2View.as_view(), name='donation-step2'),
url(r'^thank-you/', DonationSuccessView.as_view(), name='donation-success'),
]
views.py
¶
from django.views.generic import TemplateView
from fluent_pages.views import CurrentPageTemplateMixin
class DonationViewBase(CurrentPageTemplateMixin):
# There is no need to redeclare the template here,
# it's auto selected from the plugin/admin by CurrentPageTemplateMixin.
#template_name = 'pages/donation.html'
render_tab = ''
def get_context_data(self, **kwargs):
context = super(DonationViewBase, self).get_context_data(**kwargs)
context['render_tab'] = self.render_tab
return context
class DonationStep1(DonationViewBase, FormView):
"""
Success page
"""
view_url_name = 'donation-step1' # for django-parler's {% get_translated_url %}
render_tab = 'step1' # for the template
template_name = ""
# ...
class DonationSuccessView(DonationViewBase, TemplateView):
"""
Success page
"""
view_url_name = 'donation-success'
render_tab = 'success'
template_name = ""
templates/pages/donation.html
¶
{% extends "pages/base.html" %}{% load fluent_contents_tags %}
{% comment %}
This template implements a sort-of "wizard" like view.
By exposing all variations in the placeholders,
the CMS view will display tabs for each option.
{% endcomment %}
{% block main %}
<div class="constrained-subtle">
<div class="container">
{% if not render_tab %}
{% page_placeholder "donation-intro" title="Donation intro" role="m" fallback=True %}
{% elif render_tab == 'step1' %}
{% page_placeholder "donation-step1" title="Step 1" role="s" fallback=True %}
{% elif render_tab == 'success' %}
{% page_placeholder "donation-success" title="Success page" role="s" fallback=True %}
{% endif %}
</div>
</div>
</div>
{% endblock %}
This template leverages the features of django-fluent-contents. Each step can now be filled in by an staff member with CMS content. Even the form can now be added as a “Content plugin”. By using FLUENT_CONTENTS_PLACEHOLDER_CONFIG, the allowed plugin types can be limited per step. For example:
FLUENT_CONTENTS_PLACEHOLDER_CONFIG = {
# ...
# The 'pages/donation.html' template:
'donation-intro': {
'plugins': (
'DonateButtonPlugin', 'TextPlugin',
),
},
'donation-step1': {
'plugins': (
'DonationForm1Plugin', 'TextPlugin',
),
},
'giveone-success': {
'plugins': (
'ThankYouPlugin',
'TextPlugin',
'RawHtmlPlugin', # For social media embed codes
),
},
})
API documentation¶
Low-level API’s¶
This package provides internal API’s, so projects can use those to query the tree or even prefill it.
Note
When using the Python shell, make sure the activate a language first.
from django.conf import settings
from django.utils.translations import activate
activate(settings.LANGUAGE_CODE)
Query pages¶
When you query the general Page
or UrlNode
model, the pages are returned in their specific type.
>>> from fluent_pages.models import Page
>>> Page.objects.published()
<Queryset [<FluentPage: Homepage>, <BlogPage: Blog>, <FluentPage: Contact>]>
To filter the results, use one of these methods:
parent_site()
- select a different site.get_for_path()
- find the node for a path.best_match_for_path()
- find the node starting with
Tip
Finding pages by ID
When FLUENT_PAGES_KEY_CHOICES is set, specific pages can be fetched
using Page.objects.get_for_key()
.
Creating pages¶
The tree can hold different page types. Always create the specific type needed, for example:
from django.contrib.auth import get_user_model
from fluent_pages.pagetypes.flatpage.models import FlatPage
User = get_user_model()
admin = User.objects.get(active=True, username='admin')
page = FlatPage.objects.create(
# Standard fields
title="Flat test",
slug="flat-test",
status=FlatPage.PUBLISHED,
author=admin,
# Type specific fields:
content="This page is created via the API!"
)
Now the page will appear too:
>>> from fluent_pages.models import Page
>>> Page.objects.published()
<Queryset [<FluentPage: Homepage>, <BlogPage: Blog>, <FluentPage: Contact>, <FlatPage: Flat test>]>
The same approach can be used for other page types. Review the model API to see which fields can be used:
FlatPage
(providecontent
and optionally,template_name
).RedirectNode
(providenew_url
and optionally,redirect_type
).TextFile
(providecontent
and optionally,content_type
).
Pages with visible HTML content also inherit from HtmlPage
,
which makes the meta_keywords
, meta_description
and optional meta_title
available too.
Fluent content pages¶
A similar way can be used for pages with block content. This uses the django-fluent-contents and django-parler API’s too:
from django.contrib.auth import get_user_model
from fluent_pages.pagetypes.fluentpage.models import FluentPage
from fluent_contents.plugins.textitem.models import TextItem
from fluent_contents.plugins.oembeditem.models import OEmbedItem
User = get_user_model()
admin = User.objects.get(active=True, username='admin')
page = FluentPage.objects.language('en').create(
# Standard fields
title="Fluent test",
slug="fluent-test",
status=FluentPage.PUBLISHED,
author=admin,
)
# Create the placeholder
placeholder = page.create_placeholder('main')
# Create the content items
TextItem.objects.create_for_placeholder(placeholder, text="Hello, World!")
OEmbedItem.objects.create_for_placeholder(placeholder, embed_url="https://vimeo.com/channels/952478/135740366")
# Adding another language:
page.create_translation('nl')
TextItem.objects.create_for_placeholder(placeholder, language_code="nl", text="Hello, World NL!")
OEmbedItem.objects.create_for_placeholder(placeholder, language_code="nl", embed_url="https://vimeo.com/channels/952478/135740366")
The .language('en')
is not required, as the current language is selected.
However, it’s good to be explicit in case your project is multilingual.
When no language code is given to create_for_placeholder()
,
it uses the current language that the parent object (i.e. the page) has.
API documentation¶
fluent_pages.adminui¶
A set of base classes, to build custom admin pages, for your page types.
These classes are separate from the fluent_pages.admin
module on purpose.
Custom page type plugins can inherit these classes to provide their enhanced admin interface.
If this module could be called fluent_pages.admin
, it would invoke the app registry
and prevent any further model initialization.
The PageAdmin
class¶
-
fluent_pages.adminui.
PageAdmin
¶ alias of
fluent_pages.adminui.pageadmin.DefaultPageChildAdmin
-
class
fluent_pages.adminui.
DefaultPageChildAdmin
(model, admin_site, *args, **kwargs)¶ Bases:
fluent_pages.adminui.urlnodechildadmin.UrlNodeChildAdmin
The base class for administrating pages. When a custom page type implements a custom admin, use this class as its base. See the code in
fluent_pages/pagetypes/*/admin.py
for examples. To deal with model inheritence, define the fieldsets using thebase_fieldsets
option. For example:base_fieldsets = ( PageAdmin.FIELDSET_GENERAL, PageAdmin.FIELDSET_MENU, PageAdmin.FIELDSET_PUBLICATION, )
By using
base_fieldsets
instead of theModelAdmin.fieldsets
attribute, any additional fields from a derived model will be displayed in a separate fieldset automatically. The title of the fieldset is configurable with theextra_fieldset_title
attribute. It’s “Contents” by default.The admin class can be extended with mixins by defining FLUENT_PAGES_PARENT_ADMIN_MIXIN / FLUENT_PAGES_CHILD_ADMIN_MIXIN.
-
base_form
¶ alias of
PageAdminForm
-
base_model
¶ alias of
fluent_pages.models.db.Page
-
formfield_for_foreignkey
(db_field, request=None, **kwargs)¶ Get a form Field for a ForeignKey.
-
render_change_form
(request, context, add=False, change=False, form_url='', obj=None)¶ Insert the language tabs.
-
base_change_form_template
= 'admin/fluent_pages/page/base_change_form.html'¶ The default template name, which is available in the template context. Use
{% extend base_change_form_template %}
in templates to inherit from it.
-
change_form_template
¶ Dynamic property to support transition to regular models.
This automatically picks
admin/parler/change_form.html
when the admin uses a translatable model.
-
The PageAdminForm
class¶
-
class
fluent_pages.adminui.
PageAdminForm
(*args, **kwargs)¶ The base class for all admin forms.
This form validates the “Slug” and “Override URL” fields.
The HtmlPageAdmin
class¶
-
class
fluent_pages.adminui.
HtmlPageAdmin
(model, admin_site, *args, **kwargs)¶ Bases:
fluent_pages.adminui.pageadmin.DefaultPageChildAdmin
The modeladmin configured to display
HtmlPage
models. TheHtmlPage
also displays akeywords
anddescription
field.This admin class defines another fieldset:
FIELDSET_SEO
. The default fieldset layout is:base_fieldsets = ( HtmlPageAdmin.FIELDSET_GENERAL, HtmlPageAdmin.FIELDSET_SEO, HtmlPageAdmin.FIELDSET_MENU, HtmlPageAdmin.FIELDSET_PUBLICATION, )
-
formfield_for_dbfield
(db_field, **kwargs)¶ Allow formfield_overrides to contain field names too.
-
fluent_pages.adminui.utils¶
Utility functions related to admin views.
-
fluent_pages.adminui.utils.
get_page_admin_url
(page)¶ Return the admin URL for a page.
fluent_pages.extensions¶
Special classes to extend the module; e.g. page type plugins.
The extension mechanism is provided for projects that benefit from a tighter integration then the Django URLconf can provide.
The API uses a registration system.
While plugins can be easily detected via __subclasses__()
, the register approach is less magic and more explicit.
Having to do an explicit register ensures future compatibility with other API’s like reversion.
The PageTypePlugin
class¶
-
class
fluent_pages.extensions.
PageTypePlugin
¶ The base class for a page type plugin.
To create a new plugin, derive from this class and call
page_type_pool.register
to enable it. For example:from fluent_pages.extensions import PageTypePlugin, page_type_pool from mycms.models import MyCustomPage @page_type_pool.register class MyCustomPagePlugin(PageTypePlugin): model = MyCustomPage render_template = "mycustompage/example.html"
As minimal configuration, specify the
model
andrender_template
fields. Themodel
should be a subclass of thePage
model class.Note
When the plugin is registered in the
page_type_pool
, it will be instantiated only once. It is therefore not possible to store per-request state at the page type object. This is similar to the behavior of theModelAdmin
classes in Django.To customize the admin, define the
model_admin
attribute. The provided class should inherit from thePageAdmin
class.The output of a page is fully customizable in the page type plugin. By default,
render_template
will be used but you can also overrideget_render_template()
,get_context()
or evenget_response()
. The latter gives full control over theHttpResponse
to send to the client.Page types can provide additional views, relative to the location where the page is placed in the tree. A shopping module for example, can display products as sub pages. Provide an URLconf to the
urls
attribute to use this feature, and resolve those URLs using thefluent_pages.urlresolvers
module.-
model_admin
¶ alias of
fluent_pages.adminui.pageadmin.DefaultPageChildAdmin
-
response_class
¶
-
__init__
()¶ Initialize self. See help(type(self)) for accurate signature.
-
get_context
(request, page, **kwargs)¶ Return the context to use in the template defined by
render_template
(orget_render_template()
). By default, it returns the model instance asinstance
field in the template.Note that this function can also be called by custom views when those views implement the
CurrentPageMixin
orCurrentPageTemplateMixin
-
get_render_template
(request, page, **kwargs)¶ Return the template to render for the specific page or request, By default it uses the
render_template
attribute.
-
get_response
(request, page, **kwargs)¶ Render the page, and return the Django
HttpResponse
.This is the main function to generate output of the page. By default, it uses
get_render_template()
,get_context()
andresponse_class
to generate the output of the page. The behavior can be replaced completely by overriding this function.
-
get_url_resolver
()¶ Access the URL resolver of the page type.
-
get_view_response
(request, page, view_func, view_args, view_kwargs)¶ Render the custom view that was exposed by the extra plugin URL patterns. This gives the ability to add extra middleware logic.
-
can_be_root
= True¶ New in version 1.1.
Define whether this page type can be a root node.
-
can_have_children
= True¶ Defines whether users are allowed to place sub pages below this node. When
is_file
isTrue
, this is never possible.
-
child_types
= []¶ New in version 1.1.
Defines which pages can be children of this node. List of values similar to those values accepted in a model ForeignKey.
-
default_in_sitemaps
= True¶ New in version 0.9.
Tell whether the page type should be displayed in the sitemaps by default. This value can be changed for most pages in the admin interface.
-
is_file
= False¶ Defines the page type represents a file; it neither has appended slash or does it allow children.
-
render_template
= None¶ Defines the template to use for rendering the page.
-
sort_priority
= 100¶ The sorting priority for the page type in the “Add Page” dialog of the admin.
-
type_id
¶ Returns the
ContentType
id of the model.
-
type_name
¶ Return the class name of the model, this is mainly provided for templates.
-
urls
= None¶ Defines the URLs that the page provides relative to the current node. This can either be the name of a Python module with
urlpatterns
in it, or a direct inlinepatterns()
list.
-
verbose_name
¶ Returns the title for the plugin, by default it reads the
verbose_name
of the model.
-
The PageTypePool
class¶
-
class
fluent_pages.extensions.
PageTypePool
¶ The central administration of plugins.
-
__init__
()¶ Initialize self. See help(type(self)) for accurate signature.
-
get_file_types
()¶ Return the
ContentType
id’s of page types that act like files (no slash or children).
-
get_folder_types
()¶ Return the
ContentType
id’s of page types that operate as a container for sub pages.
-
get_model_classes
()¶ Return all model classes which are exposed by page types. Each model derives from
Page
.
-
get_plugin_by_model
(model_class)¶ Return the corresponding
PageTypePlugin
for a given model.
-
get_plugins
()¶ Return the
PageTypePlugin
instances which are loaded.
-
get_url_pattern_plugins
()¶ Return the
PageTypePlugin
instances that provide URL patterns.
-
get_url_pattern_types
()¶ Return the
ContentType
id’s of page types that provide URL patterns.
-
register
(plugin)¶ Make a page type plugin known by the CMS.
Parameters: plugin – The plugin class, deriving from PageTypePlugin
.The plugin will be instantiated, just like Django does this with
ModelAdmin
classes. If a plugin is already registered, this will raise aPluginAlreadyRegistered
exception.
-
fluent_pages.integration.fluent_contents¶
The base classes to create a page which can display django-fluent-contents models.
The API to interface with django-fluent-contents is public, and documented for this reason.
In fact, this module is just a tiny bridge between the page type plugins and the django-fluent-pages API.
It can be used to create custom page types that display ContentItem
objects.
The following parts are provided:
- The admin class;
admin.FluentContentsPageAdmin
- The page type model:
models.FluentContentsPage
- The plugin class:
page_type_plugins.FluentContentsPagePlugin
These classes can be imported from their respective subpackages:
from fluent_pages.integration.fluent_contents.admin import FluentContentsPageAdmin
from fluent_pages.integration.fluent_contents.models import FluentContentsPage
from fluent_pages.integration.fluent_contents.page_type_plugins import FluentContentsPagePlugin
The FluentContentsPageAdmin
class¶
-
class
fluent_pages.integration.fluent_contents.admin.
FluentContentsPageAdmin
(model, admin_site, *args, **kwargs)¶ This admin is a small binding between the pagetypes of django-fluent-pages and page contents of django-fluent-contents.
Use
{% extends base_change_form_template %}
in your page template, and it will all work properly.-
get_all_allowed_plugins
()¶ By default, all plugins are allowed, unless a placeholder puts a limit on this. The page will load much faster if the plugin types are limited globally here.
-
get_placeholder_data
(request, obj=None)¶ Read the placeholder data to display in the template. This reads
placeholder_layout
andplaceholder_layout_template
. It can be overwritten to return the layout depending on the page or request.Tip: if the object is given, this could read
obj.plugin.get_render_template(request, obj)
too.
-
get_translation_objects
(request, language_code, obj=None, inlines=True)¶ Make sure the translated ContentItem objects are also deleted when a translation is removed.
-
all_allowed_plugins
= None¶ A static list of all allowed plugin names. This is read by
get_all_allowed_plugins()
-
placeholder_layout
= None¶ A fixed defined placeholder layout, which can be defined statically. This should be a list of
PlaceholderData
objects.
-
placeholder_layout_template
= None¶ A fixed template, from which the placeholder data can be read. The
placeholder_layout
will be read automatically from the template.
-
The FluentContentsPage
class¶
-
class
fluent_pages.integration.fluent_contents.models.
FluentContentsPage
(*args, **kwargs)¶ The base model to create a Page object which hosts placeholders and content items.
Parameters: - id (AutoField) – Id
- polymorphic_ctype (ForeignKey to
ContentType
) – Polymorphic ctype - parent (PageTreeForeignKey to
UrlNode
) – Parent. You can also change the parent by dragging the page in the list. - parent_site (ForeignKey to
Site
) – Parent site - status (CharField) – Status
- publication_date (DateTimeField) – Publication date. When the page should go live, status must be “Published”.
- publication_end_date (DateTimeField) – Publication end date
- in_navigation (BooleanField) – Show in navigation
- in_sitemaps (BooleanField) – Include in search engine sitemaps
- key (SlugField) – Page identifier. A unique identifier that is used for linking to this page.
- author (ForeignKey to
User
) – Author - creation_date (DateTimeField) – Creation date
- modification_date (DateTimeField) – Last modification
- lft (PositiveIntegerField) – Lft
- rght (PositiveIntegerField) – Rght
- tree_id (PositiveIntegerField) – Tree id
- level (PositiveIntegerField) – Level
- urlnode_ptr (OneToOneField to
UrlNode
) – Urlnode ptr - placeholder_set (PlaceholderRelation) – Placeholder set
- contentitem_set (ContentItemRelation) – Contentitem set
-
create_placeholder
(slot, role='m', title=None)¶ Create a placeholder on this page.
To fill the content items, use
ContentItemModel.objects.create_for_placeholder()
.Return type: Placeholder
-
get_content_items_by_slot
(slot)¶ Return all content items of the page, which are stored in the given slot name. :rtype:
ContentItemQuerySet
-
get_placeholder_by_slot
(slot)¶ Return a placeholder that is part of this page. :rtype:
Placeholder
-
contentitem_set
¶ Model field: contentitem set, accesses the M2M
FluentContentsPage
model.
-
placeholder_set
¶ Model field: placeholder set, accesses the M2M
FluentContentsPage
model.
The FluentContentsPagePlugin
class¶
-
class
fluent_pages.integration.fluent_contents.page_type_plugins.
FluentContentsPagePlugin
¶ Base plugin to render a page with content items.
-
model_admin
¶ alias of
fluent_pages.integration.fluent_contents.admin.FluentContentsPageAdmin
-
get_render_template
(request, fluentpage, **kwargs)¶ Overwritten to automatically pick up the template used in the admin.
-
model
= None¶ Defines the model to use to store the custom fields. It must derive from
FluentContentsPage
.
-
fluent_pages.models¶
The data layer of the CMS, exposing all database models.
The objects can be imported from the main package. There are several sub packages:
db: The database models managers: Additional manager classes modeldata: Classes that expose model data in a sane way (for template designers) navigation: The menu navigation nodes (for template designers)
The UrlNode
class¶
-
class
fluent_pages.models.
UrlNode
(*args, **kwargs)¶ The base class for all nodes; a mapping of an URL to content (e.g. a HTML page, text file, blog, etc..)
Parameters: - id (AutoField) – Id
- polymorphic_ctype (ForeignKey to
ContentType
) – Polymorphic ctype - parent (PageTreeForeignKey to
UrlNode
) – Parent. You can also change the parent by dragging the page in the list. - parent_site (ForeignKey to
Site
) – Parent site - status (CharField) – Status
- publication_date (DateTimeField) – Publication date. When the page should go live, status must be “Published”.
- publication_end_date (DateTimeField) – Publication end date
- in_navigation (BooleanField) – Show in navigation
- in_sitemaps (BooleanField) – Include in search engine sitemaps
- key (SlugField) – Page identifier. A unique identifier that is used for linking to this page.
- author (ForeignKey to
User
) – Author - creation_date (DateTimeField) – Creation date
- modification_date (DateTimeField) – Last modification
- lft (PositiveIntegerField) – Lft
- rght (PositiveIntegerField) – Rght
- tree_id (PositiveIntegerField) – Tree id
- level (PositiveIntegerField) – Level
-
exception
DoesNotExist
¶
-
exception
MultipleObjectsReturned
¶
-
__init__
(*args, **kwargs)¶ Replace Django’s inheritance accessor member functions for our model (self.__class__) with our own versions. We monkey patch them until a patch can be added to Django (which would probably be very small and make all of this obsolete).
If we have inheritance of the form ModelA -> ModelB ->ModelC then Django creates accessors like this: - ModelA: modelb - ModelB: modela_ptr, modelb, modelc - ModelC: modela_ptr, modelb, modelb_ptr, modelc
These accessors allow Django (and everyone else) to travel up and down the inheritance tree for the db object at hand.
The original Django accessors use our polymorphic manager. But they should not. So we replace them with our own accessors that use our appropriate base_objects manager.
-
delete
(*args, **kwargs)¶ Calling
delete
on a node will delete it as well as its full subtree, as opposed to reattaching all the subnodes to its parent node.There are no argument specific to a MPTT model, all the arguments will be passed directly to the django’s
Model.delete
.delete
will not return anything.
-
get_absolute_url
()¶ Return the URL to this page.
-
get_absolute_urls
()¶ Return all available URLs to this page.
-
is_publication_date_active
(date=None)¶ Return whether a configured publication date is within range.
New in version 2.0.4.
-
save
(*args, **kwargs)¶ Save the model, and update caches.
-
save_translation
(translation, *args, **kwargs)¶ Update the fields associated with the translation. This also rebuilds the decedent URLs when the slug changed.
Return the breadcrumb; all parent pages leading to the current page, including current page itself.
-
can_be_root
¶ Return
True
when the page type can be used as root page.
-
can_have_children
¶ Return
True
when the node can have child nodes.
-
child_types
¶ Return a list of content type ids of nodes that can be children of this node.
-
default_url
¶ The internal implementation of
get_absolute_url()
. This function can be used when overridingget_absolute_url()
in the settings. For example:ABSOLUTE_URL_OVERRIDES = { 'fluent_pages.Page': lambda o: "http://example.com" + o.default_url }
-
is_draft
¶ Return whether the node is still a draft.
-
is_file
¶ Return
True
when the node represents a file (can’t have children, doesn’t have a layout).
-
is_first_child
¶ Return
True
when the node is the first sibling.
-
is_last_child
¶ Return
True
when the node is the last sibling.
-
is_published
¶ Return whether the node is published.
-
last_modified
¶ Return the last modification date of the page. Currently this is the last time the page was saved. This is implemented as separate property, to be extended to page content in the future.
-
page_key
¶ Ensure get_child_types is run once per plugin model.
-
plugin
¶ Access the parent plugin which renders this model.
-
url
¶ The URL of the page, provided for template code.
The Page
class¶
-
class
fluent_pages.models.
Page
(*args, **kwargs)¶ The base class for all all
UrlNode
subclasses that display pages.This is a proxy model that changes the appearance of the node in the admin. The
UrlNode
displays the URL path, while this model displays thetitle
.Parameters: - id (AutoField) – Id
- polymorphic_ctype (ForeignKey to
ContentType
) – Polymorphic ctype - parent (PageTreeForeignKey to
UrlNode
) – Parent. You can also change the parent by dragging the page in the list. - parent_site (ForeignKey to
Site
) – Parent site - status (CharField) – Status
- publication_date (DateTimeField) – Publication date. When the page should go live, status must be “Published”.
- publication_end_date (DateTimeField) – Publication end date
- in_navigation (BooleanField) – Show in navigation
- in_sitemaps (BooleanField) – Include in search engine sitemaps
- key (SlugField) – Page identifier. A unique identifier that is used for linking to this page.
- author (ForeignKey to
User
) – Author - creation_date (DateTimeField) – Creation date
- modification_date (DateTimeField) – Last modification
- lft (PositiveIntegerField) – Lft
- rght (PositiveIntegerField) – Rght
- tree_id (PositiveIntegerField) – Tree id
- level (PositiveIntegerField) – Level
-
exception
DoesNotExist
¶
-
exception
MultipleObjectsReturned
¶
The HtmlPage
class¶
-
class
fluent_pages.models.
HtmlPage
(*args, **kwargs)¶ The
HtmlPage
is the base for all page types that display HTML. This is a proxy model, which adds translatable SEO fields and a customizable title.Parameters: - id (AutoField) – Id
- polymorphic_ctype (ForeignKey to
ContentType
) – Polymorphic ctype - parent (PageTreeForeignKey to
UrlNode
) – Parent. You can also change the parent by dragging the page in the list. - parent_site (ForeignKey to
Site
) – Parent site - status (CharField) – Status
- publication_date (DateTimeField) – Publication date. When the page should go live, status must be “Published”.
- publication_end_date (DateTimeField) – Publication end date
- in_navigation (BooleanField) – Show in navigation
- in_sitemaps (BooleanField) – Include in search engine sitemaps
- key (SlugField) – Page identifier. A unique identifier that is used for linking to this page.
- author (ForeignKey to
User
) – Author - creation_date (DateTimeField) – Creation date
- modification_date (DateTimeField) – Last modification
- lft (PositiveIntegerField) – Lft
- rght (PositiveIntegerField) – Rght
- tree_id (PositiveIntegerField) – Tree id
- level (PositiveIntegerField) – Level
-
exception
DoesNotExist
¶
-
exception
MultipleObjectsReturned
¶
-
delete
(*args, **kwargs)¶ Calling
delete
on a node will delete it as well as its full subtree, as opposed to reattaching all the subnodes to its parent node.There are no argument specific to a MPTT model, all the arguments will be passed directly to the django’s
Model.delete
.delete
will not return anything.
-
meta_robots
¶ The value for the
<meta name="robots" content=".."/>
tag. It defaults tonoindex
whenin_sitemaps
isFalse
.
The PageLayout
class¶
-
class
fluent_pages.models.
PageLayout
(*args, **kwargs)¶ A
PageLayout
object defines a template that can be used by a page.Parameters: - id (AutoField) – Id
- key (SlugField) – Key. A short name to identify the layout programmatically
- title (CharField) – Title
- template_path (TemplateFilePathField) – Template file
-
exception
DoesNotExist
¶
-
exception
MultipleObjectsReturned
¶
-
get_template
()¶ Return the template to render this layout.
The UrlNodeManager
class¶
-
class
fluent_pages.models.
UrlNodeManager
¶ Extra methods attached to
UrlNode.objects
andPage.objects
.-
queryset_class
¶ alias of
UrlNodeQuerySet
-
best_match_for_path
(path, language_code=None)¶ Return the UrlNode that is the closest parent to the given path.
UrlNode.objects.best_match_for_path(‘/photos/album/2008/09’) might return the page with url ‘/photos/album/’.
Changed in version 0.9: This filter only returns the pages of the current site.
-
get_for_key
(key)¶ New in version 0.9: Return the UrlNode for the given key.
The key can be a slug-like value that was configured in
FLUENT_PAGES_KEY_CHOICES
.
-
get_for_path
(path, language_code=None)¶ Return the UrlNode for the given path. The path is expected to start with an initial slash.
Raises UrlNode.DoesNotExist when the item is not found.
Changed in version 0.9: This filter only returns the pages of the current site.
Return only pages in the navigation.
-
in_sitemaps
()¶ New in version 0.9.
Return only pages in the navigation.
-
parent_site
(site)¶ New in version 0.9: Filter to the given site.
-
published
(for_user=None)¶ Return only published pages for the current site.
Changed in version 0.9: This filter only returns the pages of the current site.
-
toplevel
()¶ Return all pages which have no parent.
Return all toplevel items, ordered by menu ordering.
When current_page is passed, the object values such as ‘is_current’ will be set.
-
url_pattern_types
()¶ Return only page types which have a custom URLpattern attached.
-
fluent_pages.templatetags.appurl_tags¶
Template tag to resolve page URLs which have an URLconf attached to them. Load this module using:
{% load appurl_tags %}
Usage:
{% appurl "my_viewname" %}
{% appurl "my_viewname" arg1 arg2 %}
{% appurl "my_viewname" kwarg1=value kwargs2=value as varname %}
fluent_pages.templatetags.fluent_pages_tags¶
Template tags to request fluent page content in the template. Load this module using:
{% load fluent_pages_tags %}
The render_breadcrumb
tag¶
Render the breadcrumb of the site, starting at the current page
.
This function either uses the default template,
or a custom template if the template
argument is provided.
{% render_breadcrumb template="fluent_pages/parts/breadcrumb.html" %}
The get_fluent_page_vars
tag¶
Introduces the site
and page
variables in the template.
This can be used for pages that are rendered by a separate application.
{% get_fluent_page_vars %}
fluent_pages.sitemaps¶
This module provides integration for the django.contrib.sitemaps
module.
This can be done using:
from fluent_pages.sitemaps import PageSitemap
sitemaps = {
'pages': PageSitemap,
}
urlpatterns += [
url(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps}),
]
The PageSitemap
class¶
-
class
fluent_pages.sitemaps.
PageSitemap
¶ The sitemap definition for the pages created with django-fluent-pages. It follows the API for the
django.contrib.sitemaps
module.-
items
()¶ Return all items of the sitemap.
-
lastmod
(urlnode)¶ Return the last modification of the page.
-
location
(urlnode)¶ Return url of a page.
-
fluent_pages.urlresolvers¶
URL Resolving for dynamically added pages.
-
fluent_pages.urlresolvers.
app_reverse
(viewname, args=None, kwargs=None, multiple=False, ignore_multiple=False, current_page=None, language_code=None)¶ Locate an URL which is located under a page type.
-
fluent_pages.urlresolvers.
mixed_reverse
(viewname, args=None, kwargs=None, current_app=None, current_page=None, language_code=None, multiple=False, ignore_multiple=False)¶ Attempt to reverse a normal URLconf URL, revert to
app_reverse()
on errors.
-
fluent_pages.urlresolvers.
clear_app_reverse_cache
()¶ Clear the cache for the
app_reverse()
function. This only has to be called when doing bulk update/delete actions that circumvent the individual model classes.
Other classes¶
-
exception
fluent_pages.urlresolvers.
MultipleReverseMatch
¶ Raised when an
app_reverse()
call returns multiple possible matches.
-
exception
fluent_pages.urlresolvers.
PageTypeNotMounted
¶ Raised when the
app_reverse()
function can’t find the required plugin in the page tree.
fluent_pages.views¶
All views of the CMS
The CurrentPageMixin
class¶
-
class
fluent_pages.views.
CurrentPageMixin
¶ Access the current page. This can be used for views which are defined as page type views in
PageTypePlugin.urls
.The template context will have the same variables as the regular page templates would have, which are: *
page
*site
* FLUENT_PAGES_BASE_TEMPLATE-
get_context_data
(**kwargs)¶ Add the plugin context to the template.
-
get_current_page
()¶ Return the current page.
-
get_view_url
()¶ When using the
ViewUrlMixin.view_url_name
feature of django-parler, this makes sure that mounted pages are also supported.It uses
fluent_pages.urlresolvers.mixed_reverse()
function to resolve theview_url_name
.
-
The CurrentPageTemplateMixin
class¶
The RobotsTxtView
class¶
-
class
fluent_pages.views.
RobotsTxtView
(**kwargs)¶ Exposing a
robots.txt
template in the Django project.Add this view to the
urls.py
:from fluent_pages.views import RobotsTxtView urlpatterns = [ # ... url(r'^robots.txt$', RobotsTxtView.as_view()), ]
Naturally, this pattern should be placed outside
i18n_patterns()
as it should appear at the top level.A
robots.txt
template is included by default, which you have override in your own project. Possible templates could look like:Simple:
Sitemap: {{ ROOT_URL }}sitemap.xml {% if ROBOTS_TXT_DISALLOW_ALL %} User-agent: * Disallow: / {% endif %}
Sitemaps per language for usage with
i18n_patterns()
:{% for language in language_codes %}Sitemap: {{ ROOT_URL }}{{ language }}/sitemap.xml {% endfor %}
Alternative:
{% for sitemap_url in sitemap_urls %}Sitemap: {{ sitemap_url }} {% endfor %}
-
get_i18n_patterns_codes
()¶ Return the possible values that
i18n_patterns()
support.
-
get_sitemap_urls
(root_url)¶ Return all possible sitemap URLs, which the template can use.
-
has_i18n_patterns_urls
()¶ Check whether something like
i18n_patterns()
is used.
-
render_to_response
(context, **response_kwargs)¶ Return a response, using the response_class for this view, with a template rendered with the given context.
Pass response_kwargs to the constructor of the response class.
-
content_type
= 'text/plain'¶ The content_type to return.
-
template_name
= 'robots.txt'¶ The template to render. You can override this template.
-
Deprecated modules¶
fluent_pages.pagetypes.fluentpage.admin¶
The FluentPageAdmin
class¶
-
class
fluent_pages.pagetypes.fluentpage.admin.
FluentPageAdmin
(model, admin_site, *args, **kwargs)¶ This admin is a small binding between the pagetypes of django-fluent-pages and page contents of django-fluent-contents.
Note
To create custom page types that combine boths apps, consider using
fluent_pages.integration.fluent_contents.admin.FluentContentsPageAdmin
instead. In fact, the code in this class concerns with the layout mechanism that is specific for this implementation.To build a variation of this page, see the API documentation of Creating a CMS system in the django-fluent-contents documentation to implement the required API’s.
-
base_form
¶ alias of
FluentPageAdminForm
-
formfield_for_foreignkey
(db_field, request=None, **kwargs)¶ Get a form Field for a ForeignKey.
-
get_layout_view
(request, id)¶ Return the metadata about a layout
-
get_page_template
(page)¶ Return the template that is associated with the page.
-
get_placeholder_data
(request, obj=None)¶ Provides a list of
fluent_contents.models.PlaceholderData
classes, that describe the contents of the template.
-
get_readonly_fields
(request, obj=None)¶ Determine which fields are readonly. This includes the shared fields if the user has no permission to change them.
-
get_urls
()¶ Introduce more urls
-
has_change_page_layout_permission
(request, obj=None)¶ Whether the user can change the page layout.
-
fluent_pages.pagetypes.fluentpage.models¶
The AbstractFluentPage
class¶
-
class
fluent_pages.pagetypes.fluentpage.models.
AbstractFluentPage
(*args, **kwargs)¶ A
`FluentPage`
represents one HTML page of the site.Note
If you really like to use the
layout
field in our custom applications, inherit from this class. Otherwise, please usefluent_pages.integration.fluent_contents.models.FluentContentsPage
instead.This class is abstract, so it’s easy to reuse the same CMS functionality in your custom page types without introducing another table/join indirection in the database. Naturally, the same layout mechanism is used. In case the
layout
should be handled differently, please consider building a variation of this page type application.Parameters: - id (AutoField) – Id
- polymorphic_ctype (ForeignKey to
ContentType
) – Polymorphic ctype - parent (PageTreeForeignKey to
UrlNode
) – Parent. You can also change the parent by dragging the page in the list. - parent_site (ForeignKey to
Site
) – Parent site - status (CharField) – Status
- publication_date (DateTimeField) – Publication date. When the page should go live, status must be “Published”.
- publication_end_date (DateTimeField) – Publication end date
- in_navigation (BooleanField) – Show in navigation
- in_sitemaps (BooleanField) – Include in search engine sitemaps
- key (SlugField) – Page identifier. A unique identifier that is used for linking to this page.
- author (ForeignKey to
User
) – Author - creation_date (DateTimeField) – Creation date
- modification_date (DateTimeField) – Last modification
- lft (PositiveIntegerField) – Lft
- rght (PositiveIntegerField) – Rght
- tree_id (PositiveIntegerField) – Tree id
- level (PositiveIntegerField) – Level
- urlnode_ptr (OneToOneField to
UrlNode
) – Urlnode ptr - layout (ForeignKey to
PageLayout
) – Layout - placeholder_set (PlaceholderRelation) – Placeholder set
- contentitem_set (ContentItemRelation) – Contentitem set
The FluentPage
class¶
-
class
fluent_pages.pagetypes.fluentpage.models.
FluentPage
(*args, **kwargs)¶ A
`FluentPage`
represents one HTML page of the site.Parameters: - id (AutoField) – Id
- polymorphic_ctype (ForeignKey to
ContentType
) – Polymorphic ctype - parent (PageTreeForeignKey to
UrlNode
) – Parent. You can also change the parent by dragging the page in the list. - parent_site (ForeignKey to
Site
) – Parent site - status (CharField) – Status
- publication_date (DateTimeField) – Publication date. When the page should go live, status must be “Published”.
- publication_end_date (DateTimeField) – Publication end date
- in_navigation (BooleanField) – Show in navigation
- in_sitemaps (BooleanField) – Include in search engine sitemaps
- key (SlugField) – Page identifier. A unique identifier that is used for linking to this page.
- author (ForeignKey to
User
) – Author - creation_date (DateTimeField) – Creation date
- modification_date (DateTimeField) – Last modification
- lft (PositiveIntegerField) – Lft
- rght (PositiveIntegerField) – Rght
- tree_id (PositiveIntegerField) – Tree id
- level (PositiveIntegerField) – Level
- urlnode_ptr (OneToOneField to
UrlNode
) – Urlnode ptr - layout (ForeignKey to
PageLayout
) – Layout - placeholder_set (PlaceholderRelation) – Placeholder set
- contentitem_set (ContentItemRelation) – Contentitem set
-
exception
DoesNotExist
¶
-
exception
MultipleObjectsReturned
¶
Package dependencies¶
This is a quick overview of all used Django packages:
![digraph G {
fontname = "Bitstream Vera Sans"
fontsize = 8
node [
fontname = "Bitstream Vera Sans"
fontsize = 8
shape = "record"
style = "filled"
fillcolor = "gray80"
]
edge [
fontname = "Bitstream Vera Sans"
fontsize = 8
]
subgraph clusterFluentPages {
label = "This package"
fluent_pages [
label = "django-fluent-pages"
]
subgraph clusterPagetypes {
label = "fluent_pages.pagetypes"
fluentpage [
label = "fluentpage"
]
flatpage [
label = "flatpage"
]
redirectnode [
label = "redirectnode"
]
textfile [
label = "textfile"
]
}
}
any_urlfield [
label = "django-any-urlfield"
]
django_wysiwyg [
label = "django-wysiwyg"
]
fluent_contents [
label = "django-fluent-contents"
]
fluent_utils [
label = "django-fluent-utils (internal)"
]
django_polymorphic [
label = "django-polymorphic"
]
django_mptt [
label = "django-mptt"
]
django_parler [
label = "django-parler"
]
django_polymorphic_tree [
label = "django-polymorphic-tree"
]
fluentpage -> fluent_contents
flatpage -> django_wysiwyg
redirectnode -> any_urlfield [style=dashed]
fluent_utils -> any_urlfield [style=dashed]
fluent_pages -> django_polymorphic_tree [lhead=clusterFluentPages]
fluent_pages -> django_parler
fluent_pages -> fluent_utils
django_polymorphic_tree -> django_polymorphic
django_polymorphic_tree -> django_mptt
}](_images/graphviz-3da9c67420d7a8b797a23b88c9c1791278fe9dee.png)
The used packages are:
- django-any-urlfield:
- An URL field which can also point to an internal Django model.
- django-fluent-contents:
- The widget engine for flexible block positions.
- django-fluent-utils:
- Internal utilities for code sharing between django-fluent modules.
- django-mptt:
The structure to store tree data in the database.
Note that django-fluent-pages doesn’t use a 100% pure MPTT tree, as it also stores a
parent_id
and_cached_url
field in the database. These fields are added for performance reasons, to quickly resolve parents, children and pages by URL.- django-parler:
- Translation support for all models.
- django-polymorphic:
- Polymorphic inheritance for Django models, it lets queries return the derived models by default.
- django-polymorphic-tree
- The tree logic, where each node can be a different model type.
- django-wysiwyg:
- A flexible WYSIWYG field, which supports various editors.
Changelog¶
Changes in 3.0.1 (2023-02-27)¶
- Added support for pure-proxy models as page type.
- Fixed unapplied changes warning in migrations for django-mptt >=0.10.
- Fixed unneeded compatibility import for
ForwardManyToOneDescriptor
. - RedirectNode: hide “show in sitemaps” link
- TextFile: hide in sitemaps by default
Changes in 3.0 (2021-11-17)¶
- Added Django 4 compatibility.
- Fixed keeping a lock unneededly when app loading was complete.
- Replaced Travis with GitHub actions.
- Dropped Python 2.7 support.
- Dropped Django 1.11, 2.0, 2.1 support.
Changes in 2.0.7 (2020-01-04)¶
- Fix Django 3.0 compatibility by removing
django.utils.six
dependency. - Bump setup requirements to ensure Django 3.0 compatibility.
Version 2.0.6 (2019-06-11)¶
- Confirmed Django 2.2 support.
- Fixed recursion in
.only()
queries, also happened with django-mptt 0.10 on deleting nodes. - Fixed pagetype plugin admin import errors, caused by ordering in
INSTALLED_APPS
. - Reformatted all files with black
Version 2.0.5 (2018-08-28)¶
- Confirmed Django 2.1 support.
- Fixed
RedirectNode
in combination with django-any-urlfield on Python 3. - Bumped dependency versions to their latest bugfix releases.
- Optimized the query logic to see whether untranslated fields should be shown.
Version 2.0.4 (2018-04-05)¶
- Added
page.is_publication_date_active()
method to check whether a page is published at a given date. - Added
Page.objects.get_for_id()
that limits the choices to the current site. - Fixed
page.is_draft
andpage.is_published
properties to takepublication_date
/publication_end_date
into account. - Fixed displayed
verbose_name
forFluentPage
, which showed “fluent page” in django-staff-toolbar. - Improved page chooser form message when selecting an unpublished page.
- Bumped minimal django-slug-preview version for proper Django 2.0 support.
Version 2.0.3 (2018-02-05)¶
- Added missing migration for the new
on_delete=SET_NULL
behavior for the author field. - Added
Meta.manager_inheritance_from_future = True
to all page subclasses that define aMeta
class. This avoids warnings in the latest django-polymorphic 2.0.1 release. It also makes sure all sub-sub classes are correctly fetched when using asubclass.objects.all()
.
Version 2.0.2 (2018-01-22)¶
- Fixed adding pages when visiting the direct child admin URL
(e.g. /admin/redirectnode/redirectnode/add/`` instead of
/admin/fluent_pages/page/add/?ct_id=
) [backported to 1.1.4].
Version 2.0.1 (2018-01-22)¶
- Fix admin list crash.
- Fixed setup classifiers.
Version 2.0 (2018-01-22)¶
- Added Django 2.0 support.
- Fixed fetching page layouts on django-polymorphic 1.3 / 2.0 [backported to 1.1.4].
- Dropped Django 1.7, 1.8 and 1.9 support, since django-polymorphic-tree also dropped this.
Version 1.1.4 (2018-01-18)¶
Backported fixes from 2.0 release in case Django 1.8 support is still needed.
- Fixed fetching page layouts on django-polymorphic 1.3 / 2.0.
- Fixed adding pages when visiting the direct child admin URL
(e.g. /admin/redirectnode/redirectnode/add/`` instead of
/admin/fluent_pages/page/add/?ct_id=
)
Version 1.1.3 (2017-11-22)¶
- Added
HtmlPage.meta_image
field to specify an Facebookog:image
for the current page - Fixed meta keywords/description showing
None
when some fields were left empty. - Fixed compatibility with upcoming django-polymorphic 2.0.
- Allow to register the model admin classes of page types directly in the admin.
- Removed
prefix_pagetypes
management command as it no never worked beyond Django 1.7.
Version 1.1.2 (2017-08-01)¶
- Added
manage.py remove_stale_pages
command that helps to clear removed page type models from the database. - Upgraded minimal django-polymorphic-tree version from 1.4 to to 1.4.1, to include a required bugfix.
- Fixed unwanted migrations created by Django 1.10+
- Fixed unselected active menu item in Django 1.11
Version 1.1.1 (2017-02-24)¶
- Fixed
native_str
usage in the admin template resolving. - Fixed more Django 1.10 issues with reverted default managers in abstract models.
This also fixes the django-fluent-blogs admin page for the
BlogPage
model on Django 1.10.
Version 1.1 (2017-02-18)¶
- Added
child_types
andcan_be_root
options to limit the allowed model types in the plugin. This allows limiting which child types can be used by plugins! - Added support for
{% appurl .. as varname %}
. - Added
ParentTranslationDoesNotExist
exception to improve error handling - Fixed Django 1.10 issue for the
FluentPage
type with an invalid default manager in the admin. - Fix multiple fallback languages support in
rebuild_page_tree
. - Fixed migration string types for Python 3.
- Fixed using
os.path.sep
inFLUENT_PAGES_TEMPLATE_DIR
- Fixed recursion bug in
RedirectNodeAdmin
. - Dropped Python 2.6 and Django 1.6 support
Note
Creating child nodes in a language that doesn’t yet exist for the parent node is no longer supported.
While past versions tried to resolve such situation with fallback URLs, it turns out to be very prone to bugs when moving page brances or changing the translated parent slug slugs.
Version 1.0.1 (2016-08-07)¶
- Fixed bug that broke Django 1.7 support.
- Avoid installing django-mptt 0.8.5, which breaks pickling deferred querysets.
Version 1.0 (2016-08-07)¶
This release provides compatibility with newer package versions. Many fixes add to the stability of this release, especially when extending it with custom page types.
Major features:
- Django 1.9 support.
- Django 1.10 support is underway (it awaits fixes in our dependencies)
- Support for multiple fallback languages.
- Nicer slug previews in the admin.
- Menu template improvements:
- Added
is_child_active
variable to fix menu highlights.- Added
draft
andactive
CSS classses.
- The
fluent_pages.pagetypes.textfile
content can be translated. - Old unmaintained languages can be redirected with the
make_language_redirects
command. - Dropped Django 1.4, 1.5 and Python 3.2 support.
- Backwards incompatible: The
FluentPageBase
class is now removed, useAbstractFluentPage
instead.
Note
Make sure to add the slug_preview
package to your INSTALLED_APPS
.
django-mptt 0.8.5 has a bug that prevents pickling deferred querysets, hence this version is explicitly excluded as requirement. Use version 0.8.4 instead.
Changes in 1.0b3 (2016-05-17)¶
- Dropped Django 1.5 support.
- Fixed displaying new empty translation page.
- Fixed page moving bug due to old caches on previous errors.
Changes in 1.0b3 (2016-05-17)¶
- Fixed showing “View on site” link for draft pages, since staff has access to it.
- Fixed
node.is_child_active
for selected parent menu’s. - Fixed applying
FLUENT_PAGES_FILTER_SITE_ID
setting in the admin. - Improved
RobotsTxtView
to handlei18n_patterns()
automatically.
Changes as of 1.0b2 (2016-02-23)¶
- Fixed published admin icon for Django 1.9
- Fixed truncating long
db_table
names. - Added
class="active"
in the default menu template for menu’s where a child item is active. - Added automatic configuration for django-staff-toolbar.
Changes as of version 1.0b1 (2015-12-30)¶
- Added Django 1.9 support
- Added translation support to the
fluent_pages.pagetypes.textfile
type, to translate the content (but not the type). - Added
draft
CSS class to unpublished menu items that are only visible for staff members. - Added
FluentPagesConfig
to use Django 1.7 appconfigs. - Added multiple fallback language support for django-parler 1.5.
- Added
make_language_redirects
management command for redirecting an unmaintained language to another. - Added
is_child_active
variable inPageNavigationNode
for menu templates. - Added django-slug-preview for nicer slug appearance in the admin.
- Improve error messages when URLs can’t be created.
- Improve performance of
PageSitemap
for sites with a lot of pages. - Temporary fix: Block moving pages to untranslated sub nodes, until a design decision can be made how to handle this.
- Temporary fix: Hide subpages when searching in the admin, to avoid errors with partial MPTT trees.
- Fixed Django 1.8 issues in the “Change Page” view.
- Fixed migrations to prevent Django from creating additional ones when settings change.
- Fixed silent behavior of using
.parent_site()
too late in an already filtered queryset. - Fixed unicode handling in
rebuild_page_tree
. - Fixed importing
mixed_reverse_lazy()
from django settings. - Fixed showing pages when there is no translation is created yet.
- Fixed JavaScript event binding for dynamic related-lookup fields.
- Fixed
welcome.json
fixture - Dropped Django 1.4 and Python 3.2 support.
- Backwards incompatible: The
FluentPageBase
class is now removed, useAbstractFluentPage
instead.
Version 0.9 (2015-04-13)¶
- Added Django 1.8 support
- Non-published pages can now be seen by staff members
- Fix initial migrations on MySQL with InnoDB/utf8 charset.
- Fix missing
robots.txt
in the PyPI package. - Fix behavior of
Page.objects.language(..).get_for_path()
andbest_match_for_path()
, use the currently selected language. This is similar to django-parler’sTranslatableModel.objects.language(..).create(..)
support. - Fix skipping mount-points in
app_reverse()
when the root is not translated. - Backwards incompatible with previous beta releases: split the
fluent_pages.integration.fluent_contents
package. You’ll need to import from the.models.
,.admin
and.page_type_plugins
explicitly. This removes many cases where projects suffered from circular import errors.
Released in 0.9c1 (2015-01-19)¶
- Fix deleting pages which have SEO fields filled in (the
HtmlPageTranslation
model). - Fix
UrlNode.DoesNotExist
exception when using{% render_breadcrumb %}
on 404 pages. - Change
slug
size to 100 characters. - Added
RobotsTxtView
for easier sitemaps integration - Added
FluentContentsPage.create_placeholder(slot)
API. - Added
--mptt-only
option tomanage.py rebuild_page_tree
command. - Added lazy-resolver functions:
app_reverse_lazy()
/mixed_reverse_lazy()
.
Released in 0.9b4 (2014-11-06)¶
- Fix South migrations for flexible
AUTH_USER_MODEL
Released in 0.9b3 (2014-11-06)¶
- Added preliminary Django 1.7 support, migrations are not fully working yet.
- Added translation support for the SEO fields (meta keywords/description/title) and redirect URL.
- All base models are proxy models now; there will be no more need to update south migrations in your own apps.
- Added
fluent_pages.integration.fluent_contents
to simplify creating custom - Added
CurrentPageMixin
andCurrentPageTemplateMixin
for custom views. - Added
HtmPage.meta_robots
property to automatically addnoindex
to pages outside the sitemaps. - Added
in_sitemaps
flag, which is now false for theRedirectNode
by default. pagetypes that reuse the django-fluent-contents integration that thefluent_pages.pagetypes.fluentpage
has. - Fixed stale translated
ContentItem
objects from django-fluent-contents when deleting a translation of a page. - Fixed support for: future >= 0.13.
- Fixed support for: django-polymorphic >= 0.6.
- Fixed support for: django-parler >= 1.2.
- API: use
FluentContentsPage
instead ofAbstractFluentPage
.
Upgrade notices:¶
Due to Django 1.7 support, the following changes had to be made:
fluent_pages.admin
is renamed tofluent_pages.adminui
.- South 1.0 is now required to run the migrations (or set
SOUTH_MIGRATION_MODULES
for all plugins).
Secondly, there were database changes to making the SEO-fields translatable. Previously, the SEO fields were provided by abstract models, requiring projects to upgrade their apps too.
All translated SEO fields are now managed in a single table, which is under the control of this app.
Fortunately, this solves any future migration issues for changes in the HtmlPage
model.
If your page types inherited from HtmlPage
, FluentContentsPage
or it’s old name FluentPage
,
you’ll have to migrate the data of your apps one more time.
The bundled pagetypes have two migrations for this: move_seo_fields
and remove_untranslatad_fields
.
The first migration moves all data to the HtmlPageTranslation
table (manually added to the datamigration).
The second migration can simply by generated with ./manage.py schemamigration <yourapp> --auto "remove_untranslatad_fields"
.
If you have overridden save_translation()
in your models, make sure to check for translation.related_name
,
as both the base object and derived object translations are passed through this method now.
The SeoPageMixin
from 0.9b1 was removed too, instead inherit directly from HtmlPage
.
Released in 0.9b2 (2014-06-28)¶
- Added Python 3 support!
- Added
key
field to allow linking to specific user-created pages (e.g. a Terms and Conditions page). This feature is only visible whenFLUENT_PAGES_KEY_CHOICES
is configured. - Fix support for
i18n_patterns()
in theoverride_url
field. - Added
hide_untranslated_menu_items
setting inFLUENT_PAGES_LANGUAGES
/PARLER_LANGUAGES
. - Added
page
variable for menu items inPageNavigationNode
. - Add “change Override URL permission” flag.
South users: run
manage.py syncdb --all
to create the permission - Fix resolving pages under their fallback language URL when a translated URL does exist.
- Fix exception in
PageNavigationNode.has_children
. - Fix moving pages in the admin list (changes were undone).
- Fix missing “ct_id” GET parmeter for Django 1.6 when filtering in the admin (due to the
_changelist_filters
parameter). - Updated dependencies to their Python 3 compatible versions.
- Optimize queries for rendering menu’s
- nodes without children no need a query in
PageNavigationNode.children
.- avoid polymorphic behavior for child menu nodes (unless the parent node was polymorphic).
Released in 0.9b1 (2014-04-14)¶
- Added multisite support.
- Added multilingual support, using django-parler.
- Added hooks for patching the admin;
FLUENT_PAGES_PARENT_ADMIN_MIXIN
andFLUENT_PAGES_CHILD_ADMIN_MIXIN
. Note that using this feature is comparable to monkey-patching, and future compatibility can’t be fully guanteed. - Added “Can change Shared fields” permission for all page types.
- Added “Can change Page layout” permission for
fluent_pages.pagetypes.fluentpage
. - Allow
formfield_overrides
to contain field names too. - API: added
SeoPageMixin
model withmeta_title
,meta_keywords
andmeta_description
fields. - API: renamed
FluentPageBase
toAbstractFluentPage
. - API: added
get_view_response
to thePageTypePlugin
class, allow adding middleware to custom views. - API: Backwards incompatible: when inheriting from the abstract
HtmlPage
model, your app needs a South migration. - Fixed calling
reverse()
on the resolved page urls. - Dropped Django 1.3 and 1.4 support.
Upgrade notices:¶
- When using custom page types that inherit from inherited from
HtmlPage
,FluentPageBase
orFluentContentsPage
, please add a South migration to your application to handle the updated fields.
The
keywords
field was renamed tometa_keywords
.The
description
field was renamed tometa_description
.The
meta_title
field was added.The South
rename_column
function can be used in the migration:db.rename_column('your_model_table', 'keywords', 'meta_keywords') db.rename_column('your_model_table', 'description', 'meta_description')
- API: renamed
FluentPageBase
toFluentContentsPage
. The old name is still available.
Version 0.8.7 (2014-12-30)¶
- Add support of django-polymorphic 0.6.
- Add
page
variable for menu items inPageNavigationNode
.
Version 0.8.6 (2014-01-21)¶
- Add
FLUENT_PAGES_DEFAULT_IN_NAVIGATION
setting to change the “in navigation” default value. - Fix django-mptt 0.6 support.
- Fix using {% appurl %} for modules with multiple results.
- Widen “modification date” column, to support other languages.
Version 0.8.5 (2013-08-15)¶
- Added intro page for empty sites.
- Support Django 1.6 transaction management.
- Fix NL translation of “Slug”.
- Fix the @admin redirect for application URLs (e.g.
/page/app-url/@admin
should redirect to/page/app-url/
). - Fix URL dispatcher for app urls when a URL prefix is used (e.g.
/en/..
) - Fix Django 1.5 custom user model support in migrations
Version 0.8.4 (2013-05-28)¶
- Fix running at Django 1.6 alpha 1
- Remove filtering pages by SITE_ID in
PageChoiceField
as there is no proper multi-site support yet. - Remove
X-Object-Type
andX-Object-Id
headers as Django 1.6 removed it due to caching issues.
Version 0.8.3 (2013-05-15)¶
- Fix circular imports for some setups that import
fluent_pages.urlresolvers
early. - Fix initial south migrations, added missing dependencies.
- Fix using
{% render_menu %}
at 404 pages.
Version 0.8.2 (2013-04-25)¶
- Add
parent
argument to{% render_menu %}
, to render sub menu’s. - Add
page
,site
variable in template of{% render_breadcrumb %}
. - Add
request
,parent
(the parent context) variables to templates of{% render_breadcrumb %}
and{% render_menu %}
. - Bump version requirement of django-mptt to 0.5.4, earlier versions have bugs.
- Fix
{% get_fluent_page_vars %}
to skip the django-haystackpage
variable. - Fix
{% get_fluent_page_vars %}
when asite
variable is already present. - Fix unit test suite in Django 1.3
Version in 0.8.1 (2013-03-07)¶
- Add “Flat page” page type.
- Add support for django-any-urlfield.
- Add
X-Object-Type
andX-Object-Id
headers to the response in development mode (similar to django.contrib.flatpages). - Add Django 1.5 Custom User model support.
- Added lots of documentation.
- Moved the template tag parsing to a separate package, django-tag-parser.
- Improve error messages on initial project setup.
- Improve ability to extend the page change_form template.
- Improve layout of keywords and description fields in the admin.
- Fixed 500 error on invalid URLs with unicode characters.
- Fixed
app_reverse()
function for Django 1.3. - Fixed
appurl
tag for template contexts without page variable. - Fixed
NavigationNode.is_active
property for sub menu nodes. - Fixed
NavigationNode.parent
property for root node. - Fixed
runtests.py
script. - Fixed
Page.objects.best_match_for_path()
for pages without a slash. - Fixed generated URL path for “file” node types in sub folders.
- Fix Django dependency in
setup.py
, moved frominstall_requires
to therequires
section. - Bump version of django-polymorphic-tree to 0.8.6 because it fixes issues with moving pages in the admin.
Version 0.8.0 (2012-11-21)¶
First public release
- Support for custom page types.
- Optional integration with django-fluent-contents.
- Refactored tree logic to django-polymorphic-tree.
- Unit tests included.