Django CMS QE

Django CMS Quick & Easy provides all important modules to run new page without a lot of coding. Aims to do it very easily and securely.

API

Settings

Settings is stored in cms_qe.settings. You can find few prepared ready-to-use environments:

  • prod,
  • dev
  • and test.

First one is for production use. It means at your app you should just import everything from there and add what you have to change (your paths and database for example). Actually the minimum config is for example this one:

import os

from cms_qe.settings.prod import *


INSTALLED_APPS += [
    'example',
]

ROOT_URLCONF = 'example.urls'
WSGI_APPLICATION = 'example.wsgi.application'

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

Security

Big goal is to have this library secure, so it has by default those main settings ready:

  • Used HTTPS with HSTS with all subdomains (but no preload).
  • Session cookie is secure and readable by HTTP only.
  • Page can be used in iframe only on the same domain (turn on only because of Django CMS).
  • CSP header to not allow any not trusted content.
  • XSS-protection header to not allow run any script submitted by form.
  • Content-type: nosniff header to not allow run any page or script uploaded as a file.
  • Use CSRF to not allow make dangerous post request outside of the page.
  • Save password very securely by latest best algorithm.
  • Use password validators to allow at least 8 characters which should not be user name, common password or all numeric.
  • Use django-axes to limit authorization (after 5 attempts is username and IP banned for one hour, can be changed by custom configuration).

Tip: when you want to just test page without final configuration, it’s good to temporally disable CSP header in your config (actually - just report but don’t block):

CSP_REPORT_ONLY = True

If your test environment do not run with SSL, then you should turn it off:

META_SITE_PROTOCOL = 'http'
SESSION_COOKIE_SECURE = False
SECURE_HSTS_SECONDS = 0

If your website is behind proxy, you have to set Django AXES to properly limit brute-force authorization by one of those options (more about that in documentation:

AXES_REVERSE_PROXY_HEADER = 'header'
AXES_NUM_PROXIES = 1

Views

cms_qe.views.errors.handler500(request: django.http.request.HttpRequest) → django.http.response.HttpResponseBase[source]

When application fail on internal error, Django CMS has problem to render any page. They don’t have any API for rendering any page so it’s impossible to get it right. Solution could be just redirect to page with given slug but it could also hang in infinite loop of redirection if there would be problem on page with slug error500.

That’s why we simply just use different template which cannot be changed in admin by user. To override default template, just create your own cms_qe/internal_error.html or change handler500 to your own view.

cms_qe.views.errors.get_error_handler(code: int) → Callable[[django.http.request.HttpRequest], django.http.response.HttpResponseBase][source]

Decorator creating error handler for specific HTTP status code.

When CMS page with slug errorCODE exists, then is shown that page, otherwise generic one. Page is not cached because of that.

Possible slugs:

  • error403 (forbidden)
  • error404 (not found)
  • error503 (service unavailable)
cms_qe.views.monitoring.get_monitoring(request)[source]

Returns JSON response with data about monitoring of application.

cms_qe.views.monitoring.get_monitoring_data()[source]

Helper to check all installed apps. It looks for function get_status in modules monitoring. If you want to add check for your module, create your file called monitoring.py and add something like this:

def get_status():
    # check
    return True  # or False or error message or dict

Result is dictionary of two keys, overall status which is True is all calls returns True, in other cases False. Second key is app_details with dictionary with key of every app which has monitoring and it’s status. Status can be bool or any message which means something is wrong.

You can also return dictionary with the details of your app. For example:

{
    'databse': True,
    'cache': 'problem to connect',
    # ...
}
cms_qe.views.security.csp_report(request: django.http.request.HttpRequest) → django.http.response.HttpResponse[source]

View handling reports by CSP headers. When there is problem by CSP, then browser fire request to this view with JSON data describing problem. It’s simply just logged as warning for later analyzing.

Template tags and filters

cms_qe.templatetags.cms_qe_filters.add_str(value, arg)[source]

Same as django.template.defaultfilters.add() but always convert to str.

cms_qe.templatetags.cms_qe_filters.get_sequence_item(value, position)[source]

Get sequence item at the position.

cms_qe.templatetags.cms_qe_filters.matches_pattern(value, pattern)[source]

The value matches pattern.

cms_qe.templatetags.cms_qe_filters.split_by_delimiter(value, delimiter)[source]

Split the value by the delimiter.

Other

class cms_qe.export.AdminField(*args, **kwds)[source]

Field to support including extra fields defined in the variable ModelAdmin.list_display. Original fields.Field is capable to get property so we need to only add support of extra fields defined on the ModelAdmin itself.

get_value(obj)[source]

Returns the value of the object’s attribute.

class cms_qe.export.ChoicesWidget(choices, *args, **kwargs)[source]

Widget that uses choice display values in place of database values

clean(value, row=None, *args, **kwargs)[source]

Returns the db value given the display value

render(value, obj=None)[source]

Returns the display value given the db value

class cms_qe.export.ExportType(mimetype, label)
label

Alias for field number 1

mimetype

Alias for field number 0

cms_qe.export.export_data(export_type, modeladmin, queryset)[source]

Export data as export_type. Supported are only those from function get_supported_export_types.

cms_qe.export.export_file(export_type, modeladmin, queryset)[source]

Same as cms_qe.export.export_data but wraps it into django.http.HttpResponse as file to download.

cms_qe.export.get_supported_export_types()[source]

Get only the export types supported by this operating system.

cms_qe.export.register_export_action(export_type, label)[source]

Helper to register export action to specific type. It dynamically creates action function and register it as admin.site.add_action with passed label.

class cms_qe.staticfiles.ManifestStaticFilesStorage(*args, manifest_storage=None, **kwargs)[source]

Custom handle of django.contrib.staticfiles.storage.ManifestStaticFilesStorage to create different URLs for every version of static file. Means when you change static file, Django’s collectstatic detects that and creates staticfiles.json with all hashes and adds them to URLs when tag static is used.

It’s good to use this when you don’t want to have problems with caches–when you change static but cache still serves the old one with new generated HTML. Thanks to this storage you can be sure that every client will use new resources needed by new page.

This custom version takes care of compatiblity of Django CMS which brings custom static tag to add owns version and then Django’s storage has problem to cooperate with it. In Django 1.10 it has to override stored_name to ignore this problem and in Django 1.11 is brought new attribute manifest_strict which has to be set to False to work as in Django 1.10.

cms_qe.utils.get_base_url(request) → str[source]

Helper to get absolute URL of application. It requires to set correctly domain of site framework.

cms_qe.utils.get_email(template: str, subject: str, to: Union[str, collections.abc.Iterable[str]], from_email: Optional[str, None] = None, **kwargs)[source]

Returns a MailerMessage instance from mailqueue. Use save() method instead of send() to send message or put it to a mailqueue.

Template should be without extension and you should create both .txt and .html version. Second one is not mandatory but is good to provide it as well.

cms_qe.utils.get_functions(module_name: str, function_name: str) → collections.abc.Iterable[tuple][source]

Get function by function_name of module_name for every installed Django app. Returns tuple of app_name and function. Example usage:

for app, func in get_functions('monitoring', 'get_status'):
    # ...
cms_qe.utils.get_module(app_name: str, module_name: str) → object[source]

Helper to load module by module_name of Django app app_name. Returns None if module does not exist.

cms_qe.utils.get_modules(module_name: str) → collections.abc.Iterable[tuple][source]

Get module by module_name for every installed Django app. Returns tuple of app_name and module. Example usage:

for app, module in get_modules('models'):
    # ...