Jinja2 is an alternative template system that can be plugged into django. It offers greator flexibility in presentation logic; if you find the django template system too restrictive, you should have a look at Jinja2 (The syntax is very similar).
In Jinja, you don't need costum tags (most of the time), because you can call functions and pass them parameters too! The only problem is that you cannot "load" functions from the template, this "loading" must be done by the code that renders the template. Same goes for filters and tests.
If you need only two or three functions/filters, no problem, you can manually add them to the Environment object; but this method doesn't really scale with real-life webapps.
This module/snippet offers a solution by allowing the user to define application-specific functions and filters and load them into the jinja2 environment automatically.
Here's how to use this:
- Install Jinja2 (for a quick & dirty installation, you can just put the jinja2 folder in a folder that's in PYTHONPATH)
- Save this python module somewhere and call it something meaningful (I have it as jinja.py in my project directory)
- Whenever you need to respond (an HttpResponse) with a template that's rendered by jinja, import this module and call
return jrespond( template_name, context )
- Any filters or functions you define in any of your applications will be readily available inside the template (see the documentation in code)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | """
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 ) )
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 3 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
It's not just the DRY principle (I myself have written some boilerplate code/decorators to make writing tags easier) but it's still bothersome to write tags, and still hard to do very simple logic in templates. for example, consider having a list of objects (say, pictures, with titles and thumbnails, like the results from google image search) and you want to arrange them in a way, say, 3 pictures per row. In Jinja you just check {% if loop.index is divisibleby(3) %} to start a new row. In django, you always have to create a new tag for every tiny bit of logic that's not included in the set of default tags/filters.
#
Please login first before commenting.