"""
Application specific functions/filters/tests in Jinja2
(note: this is not complete, but it works for me! I hope it can be useful for you)
The Integration code was originally based on
http://lethain.com/entry/2008/jul/22/replacing-django-s-template-language-with-jinja2/
But I changed it so much that nothing much is left from the original.
This module provides jinja2 integration with django that features
automatic loading of application-specific functions/filters/tests to the
environment of Jinja
(plus enabling auto escape)
To define global functions:
put a jtemp.py module in your application directory, and it will be imported
into the globals dictionary of the environment under the namespace of your
application.
so if your app is called "news" and you define a function "top_stories" in jtemp.py
then from the template, you can access it as "news.top_stories()", for example:
{% set top_stories = news.top_stories() %}
To define filters:
put a jfilters.py module in your application directory, and any function in it
that starts with "filter_" will be added to the filters dictionary of the
environment without the filter_ prefix (so filter_url will be called "url")
To define tests:
put a jtests.py module in your application directory, and any function in it that
starts with "is_" will be added to the tests dictionary of the environment without
the "is_" prefix, (so is_odd will be called "odd")
To use this, i put it in a file called jinja.py and if I need to respond to an http
request with a page that's rendered in jinja, I just say:
# from myproj.jinja import jrespond
and when I return a response:
# return jrespond( template_file_name, context )
"""
from django.conf import settings
from django.http import HttpResponse
from jinja2 import Environment, FileSystemLoader, PackageLoader, ChoiceLoader, exceptions as jexceptions
def import_module(name):
mod = __import__(name)
components = name.split('.')
for comp in components[1:]:
mod = getattr(mod, comp)
return mod
# environment setup:
# the loader here searches "template dirs", plus any templates folder in any installed app
# autoescape set to true explicitly, because in Jinja2 it's off by defualt
jenv = Environment( autoescape=True, loader = ChoiceLoader( [FileSystemLoader(settings.TEMPLATE_DIRS)] + [PackageLoader(app) for app in settings.INSTALLED_APPS] ) )
# Search for and load application specific functions, filters and tests.
for app in settings.INSTALLED_APPS: #import jtemp.py from each app and add it to globals
try:
jtemp = import_module( app + '.jtemp' )
app_name = app.split('.')[-1] #application name
#if jtemp defines jinja_name, use it as the module name in jinja, otherwise use application name
name = getattr( jtemp, 'jinja_name', app_name )
jenv.globals[name] = jtemp
except ImportError:
pass
try:
filters = import_module( app + '.jfilters' )
filter_functions = [getattr(filters,filter) for filter in dir(filters) if callable(getattr(filters,filter)) and filter.startswith("filter_")]
for func in filter_functions:
jenv.filters[getattr(func, 'jinja_name', func.__name__[7:])] = func
except ImportError:
pass
try:
tests = import_module( app + '.jtests' )
test_functions = [getattr(tests,test) for test in dir(tests) if callable(getattr(tests,test)) and test.startswith("is_")]
for func in test_functions:
jenv.tests[getattr(func, 'jinja_name', func.__name__[3:])] = func
except ImportError:
pass
def get_select_template( filenames ):
""" get or select template; accepts a file name or a list of file names """
if type( filenames ) == type([]):
for file in filenames:
try: return jenv.get_template( file )
except jexceptions.TemplateNotFound: pass
raise jexceptions.TemplateNotFound( filenames )
else:
file = filenames #for better readability
return jenv.get_template( file )
def jrender(filename, context={}):
""" renders a jinja template to a string """
template = get_select_template(filename)
return template.render(**context)
def jrespond( filename, context={}, request=None ):
""" renders a jinja template to an HttpResponse """
if( request ):
context['request'] = request
return HttpResponse( jrender( filename, context ) )