blob: 0112ed3e7215ffa2d7a592991ba8138a5614c404 [file] [log] [blame]
================
Template Loaders
================
This part of the documentation explains how to use and write a template loader.
Builtin Loaders
===============
This list contains the builtin loaders you can use without further
modification:
[[list_of_loaders]]
Loader Baseclasses
==================
With Jinja 1.1 onwards all the loaders have (except of the uncached)
baseclasses. You can use them to mix your own caching layer in. This technique
is described below. The `BaseLoader` itself is also a loader baseclass but
because it's the baseclass of all loaders it's covered in the "Developing
Loaders" section.
[[list_of_baseloaders]]
Developing Loaders
==================
Template loaders are just normal Python classes that have to provide some
functions used to load and translate templates. Because some of the tasks
a loader has to do are redundant there are some classes that make loader
development easier.
Here the implementation of a simple loader based on the `BaseLoader` from
`jinja.loaders`:
.. sourcecode:: python
import codecs
from os.path import join
from jinja.loaders import BaseLoader
from jinja.exceptions import TemplateNotFound
class SimpleLoader(BaseLoader):
def __init__(self, path):
self.path = path
def get_source(self, environment, name, parent):
filename = join(self.path, name)
if not path.exists(filename):
raise TemplateNotFound(name)
f = codecs.open(filename, 'r', environment.template_charset)
try:
return f.read()
finally:
f.close()
The functions `load` and `parse` which are a requirement for a loader are
added automatically by the `BaseLoader`. Instead of the normal `BaseLoader`
you can use one of the other base loaders that already come with a proper
`get_source` method for further modification. Those loaders however are
new in Jinja 1.1.
CachedLoaderMixin
-----------------
Additionally to the `BaseLoader` there is a mixin class called
`CachedLoaderMixin` that implements memory and disk caching of templates.
Note that you have to give it a higher priority in the MRO than the
`BaseLoader` which means that's the first base class when inheriting from it:
.. sourcecode:: python
import codecs
from os.path import join, getmtime, exists
from jinja.loaders import BaseLoaderCachedLoaderMixin
from jinja.exceptions import TemplateNotFound
class CachedLoader(CachedLoaderMixin, BaseLoader):
def __init__(self, path):
self.path = path
CachedLoaderMixin.__init__(self,
True, # use memory caching
40, # for up to 40 templates
'/tmp', # additionally save the compiled templates in /tmp
True, # and reload cached templates automatically if changed
'foo' # optional salt used to keep templates with the same
# name in the same cache folder, but from different
# loaders. New in Jinja 1.1 and can be omitted.
)
def get_source(self, environment, name, parent):
filename = join(self.path, name)
if not path.exists(filename):
raise TemplateNotFound(name)
f = codecs.open(filename, 'r', environment.template_charset)
try:
return f.read()
finally:
f.close()
def check_source_changed(self, environment, name):
fn = join(self.path, name)
if exists(fn):
return getmtime(fn)
return -1
You don't have to provide the `check_source_changed` method. If it doesn't
exist the option `auto_reload` won't have an effect. Also note that the
`check_source_changed` method must not raise an exception if the template
does not exist but return ``-1``. The return value ``-1`` is considered
"always reload" whereas ``0`` means "do not reload". The default return
value for not existing templates should be ``-1``.
For the default base classes that come with Jinja 1.1 onwards there exist
also concrete implementations that support caching. The implementation
just mixes in the `CachedLoaderMixin`.
MemcachedLoaderMixin
--------------------
*New in Jinja 1.1*
The `MemcachedLoaderMixin` class adds support for `memcached`_ caching.
There is only one builtin loader that mixes it in: The
`MemcachedFileSystemLoader`. If you need other loaders with this mixin
you can easily subclass one of the existing base loaders. Here an example
for the `FunctionLoader`:
.. sourcecode:: python
from jinja.loaders import FunctionLoader, MemcachedLoaderMixin
class MemcachedFunctionLoader(MemcachedLoaderMixin, FunctionLoader):
def __init__(self, loader_func):
BaseFunctionLoader.__init__(self, loader_func)
MemcachedLoaderMixin.__init__(self,
True, # use memcached
60 * 60 * 24 * 7, # 7 days expiration
['127.0.0.1:11211'], # the memcached hosts
'template/' # string prefix for the cache keys
)
This mixin requires the `python-memcached`_ library.
.. _memcached: http://www.danga.com/memcached/
.. _python-memcached: http://www.tummy.com/Community/software/python-memcached/
How Mixin Classes Work
======================
The idea of the cached loader mixins is that you override the `load`
method of the other base class so that it's only called to get the data
from the loader and put it into a cache and then bypass the original `load`.
This works because mixin classes, as well as the loaders are so called "new
style classes" with a MRO (method resolution order). So it's possible to
access the parent without actually knowing the name of it.
Here as small mixin class that stores everything after loading in a
dict:
.. sourcecode:: python
class SimpleCacheMixin(object):
def __init__(self):
self.__cache = {}
def load(self, environment, name, translator):
if name in self.__cache:
return self.__cache[name]
tmpl = super(SimpleCacheMixin, self).load(environment, name,
translator)
self.__cache[name] = tmpl
return tmpl
You can then mix the class in like any other mixin class. Note that
all non public attributes **must** be prefixed with two underscores to
enable the name mangling. Otherwise the mixin class could break the
internal structure of the loader.
The ``super(SimpleCacheMixin, self)`` call returns an object that looks
up all the attributes you request in all the parent classes. The
`SimpleCacheMixin` just has the `object` parent which makes it a new
style class, but as soon as a loader is mixed in it will call the
`load` method of the loader that is the other parent of the resulting
class. Here a full example.
Combining Everything
====================
Here a full example with a custom cache mixin and a custom base loader:
.. sourcecode:: python
import codecs
from os.path import join
from jinja.loaders import BaseLoader
from jinja.exceptions import TemplateNotFound
class SimpleBaseLoader(BaseLoader):
def __init__(self, path):
self.path = path
def get_source(self, environment, name, parent):
filename = join(self.path, name)
if not path.exists(filename):
raise TemplateNotFound(name)
f = codecs.open(filename, 'r', environment.template_charset)
try:
return f.read()
finally:
f.close()
class SimpleCacheMixin(object):
def __init__(self):
self.__cache = {}
def load(self, environment, name, translator):
if name in self.__cache:
return self.__cache[name]
tmpl = super(SimpleCacheMixin, self).load(environment, name,
translator)
self.__cache[name] = tmpl
return tmpl
class SimpleLoader(SimpleBaseLoader, SimpleCacheMixin):
def __init__(self, path):
SimpleBaseLoader.__init__(self, path)
SimpleCacheMixin.__init__()
You can of course put all the functionallity into the `SimpleLoader` but then
you cannot exchange parts of it without rewriting much code. In the example
above replacing the `SimpleCacheMixin` with a `MemcachedLoaderMixin` is a
matter of 20 seconds.