Daniel Banck
Webentwicklung – Webdevelopment
Webentwicklung – Webdevelopment
In the last days I tried to get Jinja2 working with Django.
Here I want to explain a bit about the integration progress.
First we need to install django and jinja2, but I won’t explain it in here, just use easy_install.
After installing you can check if everything works by importing django and jinja2 inside a python shell.
If it works we need to create a django project and a example application. (Go for the django tutorial if you don’t know how to do this).
Now we need to tell django to use jinja2 to render templates.
The best way to do this is creating a little lib with function to do this which one can import into the views.
So we create inside our project root a directory called libs and there an empty __init__.py file and a file for the jinja2 things.
Lets call it jin.py.
Inside this file we want to create a render_to_response function for jinja2 templates which can be imported from any view.
So here we go:
from django.http import HttpResponse from django.conf import settings from django.utils import translation from django.core.urlresolvers import get_callable from jinja2 import FileSystemLoader, Environment, PackageLoader, ChoiceLoader from functools import wraps loader_array = [] for pth in getattr(settings, 'TEMPLATE_DIRS', ()): loader_array.append(FileSystemLoader(pth)) for app in settings.INSTALLED_APPS: loader_array.append(PackageLoader(app)) default_mimetype = getattr(settings, 'DEFAULT_CONTENT_TYPE') global_exts = getattr(settings, 'JINJA_EXTS', ()) env = Environment(extensions=global_exts, loader=ChoiceLoader(loader_array))
This creates the basic jinja2 environment. Notice that it automatically reads the TEMPLATE_DIRS from settings.py and furthermore you can declare inside the settings file which jinja2 extensions should be loaded.
if 'jinja2.ext.i18n' in global_exts: env.install_gettext_translations(translation) global_imports = getattr(settings, 'JINJA_GLOBALS', ()) for imp in global_imports: method = get_callable(imp) method_name = getattr(method,'jinja_name',None) if not method_name == None: env.globals[method_name] = method else: env.globals[method.__name__] = method global_filters = getattr(settings, 'JINJA_FILTERS', ()) for imp in global_filters: method = get_callable(imp) method_name = getattr(method,'jinja_name',None) if not method_name == None: env.filters[method_name] = method else: env.filters[method.__name__] = method global_tests = getattr(settings, 'JINJA_TESTS', ()) for imp in global_tests: method = get_callable(imp) method_name = getattr(method,'jinja_name',None) if not method_name == None: env.tests[method_name] = method else: env.tests[method.__name__] = method
We can also declare the jinja2 globas, filters and tests inside the settings.py via adding:
JINJA_EXTS = ( 'jinja2.ext.i18n', ) JINJA_GLOBALS = ( ) JINJA_FILTERS = ( ) JINJA_TESTS = ( )
to it.
So until now our jinja2 environment contains the i18n extension and all other things added in settings.py.
Time to create a render_to_response function.
def render_to_string(filename, context={}): template = env.get_template(filename) rendered = template.render(**context) return rendered def render_to_response(filename, context={}, request=None, mimetype=default_mimetype): if request: context['request'] = request context['user'] = request.user rendered = render_to_string(filename, context) return HttpResponse(rendered,mimetype=mimetype)
This function also adds the user object to the context if a request is available.
Inside a view we can to render a template with jinja2:
from libs.jin import render_to_response def index(request): return render_to_response('app_name/index.html')
Wow. This wasn’t hard, was it?
But you might notice that the user object still isn’t available.
To get the user object back we would need to pass the request object every time to the render_to_response function. That’s really annoying and looks ugly:
return render_to_response('app_name/index.html',{},request)
So we create a decorator with some more nice features inside our jin.py:
def jin_renderer(prefix=None): def renderer(func): @wraps(func) def wrapper(request, *args, **kwargs): global default_mimetype response = func(request, *args, **kwargs) if isinstance(response, HttpResponse): return response elif isinstance(response, basestring): template_name = response context_processors = {} mimetype = default_mimetype elif isinstance(response, (tuple, list)): len_tuple = len(response) if len_tuple == 2: template_name, context_processors = response mimetype = default_mimetype elif len_tuple == 3: template_name, context_processors, mimetype = response if prefix: if isinstance(template_name, (list, tuple)): template_name = map(correct_path, template_name) else: template_name = correct_path(template_name) return render_to_response(template_name,context_processors,request,mimetype) return wrapper def correct_path(template_name): if template_name.startswith('/'): return template_name[1:] return '%s%s' % (prefix, template_name) return renderer
When using this renderer the request object is always available and we can also define a template prefix path.
Using this new renderer is very easy, too:
from libs.jin import jin_renderer render_to_html = jin_renderer('app_name/') @render_to_html def index(request): return 'index.html',{'test':'some context test'}
Now the index.html side your templates/app_name/ will be rendered.
These sites helped me creating the code:
http://jinja.pocoo.org/2/documentation/
http://www.djangosnippets.org/snippets/1112/
http://www.djangosnippets.org/snippets/1061/
http://tinyurl.com/57mojt
April 28, 2009 - 2:53 am
Hi,
Last line of the last code snippet is wrong.
I think it should be:
return ‘index.html’,{‘test’:’some context test’}
April 29, 2009 - 1:31 pm
Thanks! I’ve corrected it.
June 30, 2009 - 11:23 pm
interesting post, will come back here, bookmarked your site
September 10, 2009 - 1:46 pm
Thanks, i’ve seeked for this two lines to use jinja2 with django:
from django.utils import translation
env.install_gettext_translations(translation)
March 5, 2010 - 12:52 am
When using the the code you provided above, I run into the error:
global name ‘env’ is not defined.
Anything you can think of that would cause this error?
March 5, 2010 - 12:58 am
Maybe you missed this line, Mike:
env = Environment(extensions=global_exts, loader=ChoiceLoader(loader_array))
Thats the definition of ‘env’. Missing this would case your error.
March 5, 2010 - 4:11 am
Actually, that line’s in there. It’s in my __init__.py under the libs folder in my project.
March 5, 2010 - 9:52 am
Did you import ‘env’?
And why you wrote that in the __init__.py? You should keep this file as small as possible.