Jinja2 template loader for django
July 17, 2018
Python

  • jinjalink_loader.py
  • settings.py
  • views.py
from django.core.urlresolvers import get_callable
from django.template import TemplateDoesNotExist
from django.template.loader import BaseLoader
from django.template.context import BaseContext
from django.conf import settings

import jinja2


def monkey_patch_django():
    """
    Patching some django objects to make them "safe" for jinja's escape() function.
    Good for us it uses __html__() method.
    """
    # Django's SafeString and SafeUnicode should not be escaped:
    from django.utils.safestring import SafeData
    SafeData.__html__ = lambda self: self

    from jinja2 import escape
    from django.forms import BaseForm, Media
    from django.forms.forms import BoundField
    from django.forms.formsets import BaseFormSet
    from django.forms.util import ErrorDict, ErrorList

    # If unicode returns SafeData, then escape will pass it outside unmodified thanks to patch above
    # If it's just a string it will be escaped
    for cls in (BaseForm, Media, BoundField, BaseFormSet, ErrorDict, ErrorList):
        cls.__html__ = lambda self: escape(unicode(self))


class Template(object):
    """
    A container for jinja2 Template class
    """
    def __init__(self, template, origin=None, name='<Unknown Template>'):
        self.template = template
        self.origin = origin
        self.name = name

    def render(self, context):
        # make a flat dict from django Context
        if isinstance(context, BaseContext):
            d = {}
            for u in context.dicts:
                d.update(u)
        else:
            d = context
        return self.template.render(d)


class Loader(BaseLoader):
    is_usable = True

    def __init__(self, *args, **kwargs):
        """
        Creating jinja2 environment
        """
        assert hasattr(settings, 'TEMPLATE_DIRS'), 'Jinja2 template loader needs TEMPLATE_DIRS setting'
        monkey_patch_django()

        extras = self._get_env_extras()

        options = getattr(settings, 'JINJA2_ENVIRONMENT_OPTIONS', {})
        options['extensions'] = extras['extensions']
        options['loader'] = jinja2.FileSystemLoader(settings.TEMPLATE_DIRS)

        ### Some special tuning of jinja2 environment goes here
        # Number of compiled jinja2 templates in process memory
        options['cache_size'] = -1
        # Check whether template file is changed only in development
        #options['auto_reload'] = settings.DEBUG
        # Use jinja's bytecode cache
        options['bytecode_cache'] = jinja2.FileSystemBytecodeCache(settings.HOME_DIR + '/tmp/jinja_cache')

        self.env = jinja2.Environment(**options)

        self.env.filters.update(extras['filters'])
        self.env.globals.update(extras['globals'])
        self.env.tests.update(extras['tests'])

    def _get_env_extras(self):
        """
        Creates a dict of extensions, filters, globals and tests from settings
        """
        extensions, filters, objects, tests = [], {}, {}, {}

        # add the globally defined extension list
        extensions.extend(list(getattr(settings, 'JINJA2_EXTENSIONS', [])))

        # parse filters, globals and tests settings
        def from_setting(setting):
            retval = {}
            setting = getattr(settings, setting, {})
            if isinstance(setting, dict):
                for key, value in setting.iteritems():
                    retval[key] = callable(value) and value or get_callable(value)
            else:
                for value in setting:
                    value = callable(value) and value or get_callable(value)
                    retval[value.__name__] = value
            return retval

        filters.update(from_setting('JINJA2_FILTERS'))
        objects.update(from_setting('JINJA2_GLOBALS'))
        tests.update(from_setting('JINJA2_TESTS'))

        return dict(
            extensions=extensions,
            filters=filters,
            globals=objects,
            tests=tests,
        )

    def load_template(self, template_name, template_dirs=None):
        # Leave .html extension for django template (admin, contrib, etc)
        if template_name.endswith('.html'):
            raise TemplateDoesNotExist(template_name)
        try:
            template = self.env.get_template(template_name)
            return Template(template, None, template_name), None
        except jinja2.TemplateNotFound:
            raise TemplateDoesNotExist(template_name)

    def load_template_source(self, template_name, template_dirs=None):
        # Leave .html extension for django template (admin, contrib, etc)
        if template_name.endswith('.html'):
            raise TemplateDoesNotExist(template_name)
        try:
            source, filename, uptodate = self.env.loader.get_source(self.env, template_name)
            return source, filename
        except jinja2.TemplateNotFound:
            raise TemplateDoesNotExist(template_name)

Reach out

© 2024 Chuma Umenze. Some rights reserved.