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.
"""
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
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):
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)
options['cache_size'] = -1
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 = [], {}, {}, {}
extensions.extend(list(getattr(settings, 'JINJA2_EXTENSIONS', [])))
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):
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):
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)