Python Imports in Templates

It’s easy to import Python and PyPI (pip) modules in your Mako templates.

Use a template-level block:
<% from datetime import datetime %>
or a module-level block:
<%! from decimal import Decimal %>

The Gory Details

When Mako compiles templates, it actually writes a Python code file: all of your HTML code goes into a function named def render_body(). It’s actually this function that gets run each time the template is “rendered”. This template:

app/templates/template.html
<%! from decimal import Decimal %>
<% from datetime import datetime %>
<h1>My Page</h1>
The even numbers:
%for i in range(10):
    %if i % 2 == 0:
        <p>${ i }: Mako is the bomb, amirite?!?</p>
    %endif
%endfor

Turns into the following function. Note where the two import statements are placed. That’s the full power of the exclamation point!

app/templates/__dmpcache__/template.html.py:
from decimal import Decimal

def render_body(context,**pageargs):
    __M_caller = context.caller_stack._push_frame()
    try:
        __M_locals = __M_dict_builtin(pageargs=pageargs)
        self = context.get('self', UNDEFINED)
        range = context.get('range', UNDEFINED)
        __M_writer = context.writer()
        __M_writer('\n')

        from datetime import datetime

        __M_locals_builtin_stored = __M_locals_builtin()
        __M_locals.update(__M_dict_builtin([(__M_key, __M_locals_builtin_stored[__M_key]) for __M_key in ['datetime'] if __M_key in __M_locals_builtin_stored]))
        __M_writer('\n<h1>My Page</h1>\nThe even numbers:\n')
        for i in range(10):
            if i % 2 == 0:
                __M_writer('        <p>')
                __M_writer(django_mako_plus.ExpressionPostProcessor(self)( i ))
                __M_writer(': Mako is the bomb, amirite?!?</p>\n')
        return ''
    finally:
        context.caller_stack._pop_frame()

Template-level blocks are placed within the body of the render_body method, while module-level blocks are placed at the top of the file. In effect, it’s the same as writing an import statement within a function vs. an import statement at the top of a module.

Although it probably doesn’t matter too much, the module-level block <%! import ... %> is usually best for import statements.

Global Template Imports

There may be some modules, such as re or decimal that are so useful you want them available in every template of your site. In settings.py, add these to the DEFAULT_TEMPLATE_IMPORTS variable:

TEMPLATES = [
    {
        'NAME': 'django_mako_plus',
        'BACKEND': 'django_mako_plus.MakoTemplates',
        'OPTIONS': {
            DEFAULT_TEMPLATE_IMPORTS = [
                'import os, os.path, re',
                'from decimal import Decimal',
            ],
            ...
        }
    }
]

Any entries in this list will be automatically included in templates throughout all apps of your site. With the above imports, you’ll be able to use re and Decimal and os and os.path anywhere in any .html, .cssm, and .jsm file.

Whenever you modify DMP settings, and especially in this case, be sure to clean out your cached templates. Mako needs to regenerate all of your templates with the new import statements at the top. See the next section on cleaning up for the command.

Cleaning Up

DMP caches its compiled mako+templates in subdirectories of each app. The default locations for each app are app/templates/__dmpcache__, app/scripts/__dmpcache__, and app/styles/__dmpcache__, although the exact name depends on your settings.py. Normally, these cache directories are hidden and warrant your utmost apathy. However, there are times when DMP fails to update a cached template as it should. Or you might just need a pristine project without these generated files. This can be done with a Unix find command or through DMP’s dmp_cleanup management command:

# see what would be be done without actually deleting any cache folders
python3 manage.py dmp_cleanup --trial-run

# really delete the folders
python3 manage.py dmp_cleanup

With this management command, add --verbose to the command to include messages about skipped files, and add --quiet to silence all messages (except errors).