jinja2API三:自动转义、标识符、未定义类型、上下文、加载器

一、自动转义

自动转义的首选途径就是启用 自动转义扩展 并为自动转义配置一个合适的默认值。这使得在单个模板基础上开关自动转义成为 可能(比如 HTML 对 文本)

这里推荐为以 .html 、 .htm 、 .xml 以及 .xhtml 的模板开启 自动转义 ,并对所有其它扩展名禁用:

def guess_autoescape(template_name):
    if template_name is None or '.' not in template_name:
        return False
    ext = template_name.rsplit('.', 1)[1]
    return ext in ('html', 'htm', 'xml')

env = Environment(autoescape=guess_autoescape,
                  loader=PackageLoader('mypackage'),
                  extensions=['jinja2.ext.autoescape'])

假设实现一个自动转义函数,确保你也视 None 为有效模板名接受。这会在从字符 串生成模板时传递。

可以用 autoescape 块在模板内临时地更改这种行为。

二、未定义类型

这些类可以用作未定义类型。 Environment 的构造函数接受一个可以是 那些类或一个 Undefined 的自定义子类的 undefined 参数。无论何时, 这些对象创建或返回时,模板引擎都不能查出其名称或访问其属性。

未定义值上的 某些操作之后是允许的,而其它的会失败。

最接近常规 Python 行为的是 StrictUndefined ,如果它是一个未定义对象, 它不允许除了测试之外的一切操作。

class jinja2.Undefined

The default undefined type. This undefined type can be printed and iterated over,

but every other access will raise an UndefinedError:

>>> foo = Undefined(name='foo')
>>> str(foo)
''
>>> not foo
True
>>> foo + 42
Traceback (most recent call last):
  ...
UndefinedError: 'foo' is undefined
_undefined_hint

None 或给未定义对象的错误消息 unicode 字符串。

_undefined_obj

None 或引起未定义对象创建的对象(例如一个属性不存在)。

_undefined_name

未定义变量/属性的名称,如果没有此类信息,留为 None 。

_undefined_exception

未定义对象想要抛出的异常。这通常是 UndefinedError 或 SecurityError 之一。

_fail_with_undefined_error(*args**kwargs)

参数任意,调用这个方法时会抛出带有由未定义对象上存储的未定义 hint 生成的错误信息的 _undefined_exception 异常。

class jinja2.DebugUndefined

An undefined that returns the debug info when printed.

>>> foo = DebugUndefined(name='foo')
>>> str(foo)
'{{ foo }}'
>>> not foo
True
>>> foo + 42
Traceback (most recent call last):
  ...
UndefinedError: 'foo' is undefined

class jinja2.StrictUndefined

An undefined that barks on print and iteration as well as boolean tests and all kinds of comparisons. In other words: you can do nothing with it except checking if it’s defined using the defined test.

>>> foo = StrictUndefined(name='foo')
>>> str(foo)
Traceback (most recent call last):
  ...
UndefinedError: 'foo' is undefined
>>> not foo
Traceback (most recent call last):
  ...
UndefinedError: 'foo' is undefined
>>> foo + 42
Traceback (most recent call last):
  ...
UndefinedError: 'foo' is undefined

 未定义对象由调用 undefined 创建。

 

实现

Undefined 对象通过重载特殊的 __underscore__ 方法实现。

例如 默认的 Undefined 类实现 __unicode__ 为返回一个空字符串,但 __int__ 和其它会始终抛出异常。你可以自己通过返回 0 实现转换为 int:

class NullUndefined(Undefined):
    def __int__(self):
        return 0
    def __float__(self):
        return 0.0

要禁用一个方法,重载它并抛出 _undefined_exception 。因 为这在未定义对象中非常常用,未定义对象有辅助方法 _fail_with_undefined_error() 自动抛出错误。这里的一个类 工作类似正规的 Undefined ,但它在迭代时阻塞:

class NonIterableUndefined(Undefined):
__iter__ = Undefined._fail_with_undefined_error

 

三、上下文

 class jinja2.runtime.Context

The template context holds the variables of a template. It stores the values passed to the template and also the names the template exports. Creating instances is neither supported nor useful as it’s created automatically at various stages of the template evaluation and should not be created by hand.

The context is immutable. Modifications on parent must not happen and modifications on vars are allowed from generated template code only. Template filters and global functions marked as contextfunction()s get the active context passed as first argument and are allowed to access the context read-only.

The template context supports read only dict operations (getkeysvaluesitemsiterkeysitervaluesiteritems__getitem____contains__). Additionally there is a resolve() method that doesn’t fail with a KeyError but returns an Undefinedobject for missing variables.

parent

一个模板查找的只读全局变量的词典。这些变量可能来自另一个 Context ,或是 Environment.globals ,或是 Template.globals ,或指向一个由全局变量和传递到渲染函数的变 量联立的字典。它一定不能被修改。

vars

模板局域变量。这个列表包含环境和来自 parent 范围的上下文函数 以及局域修改和从模板中导出的变量。模板会在模板求值时修改这个字典, 但过滤器和上下文函数不允许修改它。

environment

加载该模板的环境

exported_vars

这设定了所有模板导出量的名称。名称对应的值在 vars 字典中。 可以用 get_exported() 获取一份导出变量的拷贝字典。

name

拥有此上下文的模板的载入名。

blocks

模板中块当前映射的字典。字典中的键是块名称,值是注册的块的列表。每个 列表的最后一项是当前活动的块(继承链中最新的)。

eval_ctx

当前的 求值上下文 。

call(callable*args**kwargs)

Call the callable with the arguments and keyword arguments provided but inject the active context or environment as first argument if the callable is a contextfunction() or environmentfunction().

get_all()

Return a copy of the complete context as dict including the exported variables.

get_exported()

Get a new dict with the exported variables.

resolve(key)

Looks up a variable like __getitem__ or get but returns an Undefined object with the name of the name looked up.

实现

Python frame 中的局域变量在函数中是不可变的,出于同样的原因,上下文是不可 变的。 Jinja2 和 Python 都不把上下文/ frame 作为变量的数据存储,而只作为 主要的数据源。

当模板访问一个模板中没有定义的变量时, Jinja2 在上下文中查找变量,此后, 这个变量被视为其是在模板中定义得一样。

四、加载器

加载器负责从诸如文件系统的资源加载模板。环境会把编译的模块像 Python 的 sys.modules 一样保持在内存中。与 sys.models 不同,无论如何这个 缓存默认有大小限制,且模板会自动重新加载。 所有的加载器都是 BaseLoader 的子类。如果你想要创建自己的加载器,继 承 BaseLoader 并重载 get_source 。

class jinja2.BaseLoader

Baseclass for all loaders. Subclass this and override get_source to implement a custom loading mechanism. The environment provides a get_template method that calls the loader’s load method to get the Template object.

A very basic example for a loader that looks up templates on the file system could look like this:

from jinja2 import BaseLoader, TemplateNotFound
from os.path import join, exists, getmtime

class MyLoader(BaseLoader):

    def __init__(self, path):
        self.path = path

    def get_source(self, environment, template):
        path = join(self.path, template)
        if not exists(path):
            raise TemplateNotFound(template)
        mtime = getmtime(path)
        with file(path) as f:
            source = f.read().decode('utf-8')
        return source, path, lambda: mtime == getmtime(path)
get_source(environmenttemplate)

Get the template source, filename and reload helper for a template. It’s passed the environment and template name and has to return a tuple in the form (source, filename, uptodate) or raise a TemplateNotFound error if it can’t locate the template.

The source part of the returned tuple must be the source of the template as unicode string or a ASCII bytestring. The filename should be the name of the file on the filesystem if it was loaded from there, otherwise None. The filename is used by python for the tracebacks if no loader extension is used.

The last item in the tuple is the uptodate function. If auto reloading is enabled it’s always called to check if the template changed. No arguments are passed so the function must store the old state somewhere (for example in a closure). If it returns False the template will be reloaded.

load(environmentnameglobals=None)

Loads a template. This method looks up the template in the cache or loads one by calling get_source(). Subclasses should not override this method as loaders working on collections of other loaders (such as PrefixLoader or ChoiceLoader) will not call this method but get_source directly.

这里有一个 Jinja2 提供的内置加载器的列表:

class jinja2.FileSystemLoader(searchpathencoding='utf-8')

Loads templates from the file system. This loader can find templates in folders on the file system and is the preferred way to load them.

The loader takes the path to the templates as string, or if multiple locations are wanted a list of them which is then looked up in the given order:

>>> loader = FileSystemLoader('/path/to/templates')
>>> loader = FileSystemLoader(['/path/to/templates', '/other/path'])

Per default the template encoding is 'utf-8' which can be changed by setting the encoding parameter to something else.

class jinja2.PackageLoader(package_namepackage_path='templates'encoding='utf-8')

Load templates from python eggs or packages. It is constructed with the name of the python package and the path to the templates in that package:

loader = PackageLoader('mypackage', 'views')

If the package path is not given, 'templates' is assumed.

Per default the template encoding is 'utf-8' which can be changed by setting the encoding parameter to something else. Due to the nature of eggs it’s only possible to reload templates if the package was loaded from the file system and not a zip file.

class jinja2.DictLoader(mapping)

Loads a template from a python dict. It’s passed a dict of unicode strings bound to template names. This loader is useful for unittesting:

>>> loader = DictLoader({'index.html': 'source here'})

Because auto reloading is rarely useful this is disabled per default.

class jinja2.FunctionLoader(load_func)

A loader that is passed a function which does the loading. The function becomes the name of the template passed and has to return either an unicode string with the template source, a tuple in the form (source, filename, uptodatefunc) or None if the template does not exist.

>>> def load_template(name):
...     if name == 'index.html':
...         return '...'
...
>>> loader = FunctionLoader(load_template)

The uptodatefunc is a function that is called if autoreload is enabled and has to return True if the template is still up to date. For more details have a look at BaseLoader.get_source() which has the same return value.

class jinja2.PrefixLoader(mappingdelimiter='/')

A loader that is passed a dict of loaders where each loader is bound to a prefix. The prefix is delimited from the template by a slash per default, which can be changed by setting the delimiter argument to something else:

loader = PrefixLoader({
    'app1':     PackageLoader('mypackage.app1'),
    'app2':     PackageLoader('mypackage.app2')
})

By loading 'app1/index.html' the file from the app1 package is loaded, by loading 'app2/index.html' the file from the second.

class jinja2.ChoiceLoader(loaders)

This loader works like the PrefixLoader just that no prefix is specified. If a template could not be found by one loader the next one is tried.

>>> loader = ChoiceLoader([
...     FileSystemLoader('/path/to/user/templates'),
...     FileSystemLoader('/path/to/system/templates')
... ])

This is useful if you want to allow users to override builtin templates from a different location.

class jinja2.ModuleLoader(path)

This loader loads templates from precompiled templates.

Example usage:

>>> loader = ChoiceLoader([
...     ModuleLoader('/path/to/compiled/templates'),
...     FileSystemLoader('/path/to/templates')
... ])

Templates can be precompiled with Environment.compile_templates().

 

posted on 2015-07-30 16:17  myworldworld  阅读(312)  评论(0)    收藏  举报

导航