zoukankan      html  css  js  c++  java
  • tornado 学习笔记9 Tornado web 框架---模板(template)功能分析

            Tornado模板系统是将模板编译成Python代码。

            最基本的使用方式:

    t = template.Template("<html>{{ myvalue }}</html>")
    print t.generate(myvalue="XXX")

           Loader这个类加载根目录的模板,然后缓存编译好的模板。

           tornado模板系统不像其他模板系统,我们不会在表达式上设置任何限制。If 和for语句块都会被精确地转成Python代码,所以你可以像下面一个使用复杂的表达式。

    {% for student in [p for p in people if p.student and p.age > 23] %}
      <li>{{ escape(student.name) }}</li>
    {% end %}

           

    ### Python code
    def add(x, y):
       return x + y
    template.execute(add=add)
    
    ### The template
    {{ add(1, 2) }}

            默认情况下,我们提供escape,url_escape,json_encode以及squeeze函数给所有的模板。

           一般情况下,应用不会创建Template或者Loader实例,而是使用tornado.web.RquestHandler的 render以及render_string 方法,这两个方法根据Application setting的template_path选项的值自动加载模板。

           以 _tt_ 开始的变量名称是被模板系统保留的,应用程序不能使用。

    9.1 语法

              模板表达式使用双方括号{{…}}包围。里面内容可以是任何python的表达式,里面的内容根据当前的autoescape设置而决定是否进行转义。其他模板指令使用{%...%}.如果你需要将{{ 或者{%原样输出,你可以使用{{!{%!进行转义。

             注释部分使用{#...#},从而注释部分会被忽略,而不会输出。

             {% apply *function* %} ….{% end %}

             在apply 和end之间对模板的所有输出应用函数。比如:

             {% apply linkify %}{{name}} said: {{message}}{% end %}

             {% block *name* %}...{% end %} 表示一个命名的、可以被替换的块看,这个语法是针对模板继承的使用 {% extends %}。在父模板中的块会被子模板同名的块中内容替换。比如:

    <!-- base.html -->
    
    <title>{% block title %}Default title{% end %}</title>
    
    <!-- mypage.html -->
    
    {% extends "base.html" %}
    
    {% block title %}My page title{% end %}

             {% comment ... %} 表示注释,是不会被输出的。注意这里没有{% end %}标签。

            {% extends *filename* %} 继承模板。

            {% for *var* in *expr* %}...{% end %} 跟Python for循环语句相同。{% break %}和{% continue %} 可以在循环内使用。

           {% from *x* import *y* %}  跟Python import语句一样

            {% if *condition* %}...{% elif *condition* %}...{% else %}...{% end %} 条件语句

             {% import *module* %} 跟Python import语句一样

             {% include *filename* %}引用其他模板文件。这个引用的文件可以看到所有的本地变量,因为他们都被直接复制。相反地,{% module Template(filename, **kwargs) %} 被用来包含其他在隔离命名空间的模板。

             {% set *x* = *y* %}设置值

            {% try %}...{% except %}...{% else %}...{% finally %}...{% end %}跟Python的异常处理一样。

             {% while *condition* %}... {% end %}跟Python的while语句一样。

    9.2 关联的类

    tornado.template.Template

        源代码位于site-packages > tornado > template.py,这个类表示已编译好的模板。

    • 构造函数:
    def __init__(self, template_string, name="<string>", loader=None,
                 compress_whitespace=None, autoescape=_UNSET):

       参数:

           template_string :模板字符串

           name :模板名称

           loader: 模板加载器

           compress_whitespace :是否压缩空白字符

           autoescape :是否自动转义

       实现代码:

    self.name = name
    if compress_whitespace is None:
        compress_whitespace = name.endswith(".html") or 
            name.endswith(".js")
    if autoescape is not _UNSET:
        self.autoescape = autoescape
    elif loader:
        self.autoescape = loader.autoescape
    else:
        self.autoescape = _DEFAULT_AUTOESCAPE
    self.namespace = loader.namespace if loader else {}
    reader = _TemplateReader(name, escape.native_str(template_string))
    self.file = _File(self, _parse(reader, self))
    self.code = self._generate_python(loader, compress_whitespace)
    self.loader = loader
    try:
        # Under python2.5, the fake filename used here must match
        # the module name used in __name__ below.
        # The dont_inherit flag prevents template.py's future imports
        # from being applied to the generated code.
        self.compiled = compile(
            escape.to_unicode(self.code),
            "%s.generated.py" % self.name.replace('.', '_'),
            "exec", dont_inherit=True)
    except Exception:
        formatted_code = _format_code(self.code).rstrip()
        app_log.error("%s code:
    %s", self.name, formatted_code)
        raise

       处理过程:

          (1) compress_whitespace的值进行设置。如果是None,如果模板是html或者js文件,则compress_whitespace 为True.

          (2) 对 autoescape 的设置。autoescape是一个函数,默认为“xhtml_escape”函数

          (3) 对namespace 命名空间的设置。如果设置了loader,则为loader的namespace.

       (4) 设置内部类_TemplateReader类型的reader.

       (5) 设置file、code属性。

       (6) 最后调用compile方法,进行模板编译。

    • generate方法

    def generate(self, **kwargs):
        """Generate this template with the given arguments."""
        namespace = {
            "escape": escape.xhtml_escape,
            "xhtml_escape": escape.xhtml_escape,
            "url_escape": escape.url_escape,
            "json_encode": escape.json_encode,
            "squeeze": escape.squeeze,
            "linkify": escape.linkify,
            "datetime": datetime,
            "_tt_utf8": escape.utf8,  # for internal use
            "_tt_string_types": (unicode_type, bytes),
            # __name__ and __loader__ allow the traceback mechanism to find
            # the generated source code.
            "__name__": self.name.replace('.', '_'),
            "__loader__": ObjectDict(get_source=lambda name: self.code),
        }
        namespace.update(self.namespace)
        namespace.update(kwargs)
        exec_in(self.compiled, namespace)
        execute = namespace["_tt_execute"]
        # Clear the traceback module's cache of source data now that
        # we've generated a new template (mainly for this module's
        # unittests, where different tests reuse the same name).
        linecache.clearcache()
        return execute()

           作用:根据给定的参数,生成模板,最后是生成模板的结果,是一个字符串。

           参数:

                 kwargs:是一个namespace的字典,都是一个方法的映射。

    tornado.template.BaseLoader

             模板加载器的基类。

             当你使用像 “{% extends %}”、{% include% }这些模板语法时,你必须使用模板加载器。当模板第一次加载完毕后,加载器会缓存这些模板。

    • 构造函数:
    def __init__(self, autoescape=_DEFAULT_AUTOESCAPE, namespace=None):
        """``autoescape`` must be either None or a string naming a function
        in the template namespace, such as "xhtml_escape".
        """
        self.autoescape = autoescape
        self.namespace = namespace or {}
        self.templates = {}
        # self.lock protects self.templates.  It's a reentrant lock
        # because templates may load other templates via `include` or
        # `extends`.  Note that thanks to the GIL this code would be safe
        # even without the lock, but could lead to wasted work as multiple
        # threads tried to compile the same template simultaneously.
        self.lock = threading.RLock()

           参数:

                 autoescape: 是否自动转义。要么是None值,要么是在模板namespace中的函数名,比如是”xhtml_escape”

                 namespace:模板中使用的方法的映射字典。

          处理过程:

               主要是对相关属性的初始化。注意,最后一句self.lock= threading.RLock() 是为了考虑多线程并发的存在,加上线程锁。

    • reset 方法

                  重设加载器的模板,清除缓存

    • resolve_path方法

                  将模板的相对路径转换成绝对路径,由子类继承

    • load方法

                加载模板。调用了resolve_path以及_create_template方法,这两个方法都有子类实现。

    def load(self, name, parent_path=None):
        """Loads a template."""
        name = self.resolve_path(name, parent_path=parent_path)
        with self.lock:
            if name not in self.templates:
                self.templates[name] = self._create_template(name)
            return self.templates[name]

    tornado.template.Loader

            模板加载器,继承BaseLoader,从单一根路径加载。

    • 构造函数:
    def __init__(self, root_directory, **kwargs):
        super(Loader, self).__init__(**kwargs)
        self.root = os.path.abspath(root_directory)

           参数:

                  root_directory:根路径

                  kwargs:参数字典

           处理过程:

                  调用父类初始化过程,同时设置root属性,为绝对路径

    • resolve_path方法

                  实现BaseLoader父类中的方法。

    def resolve_path(self, name, parent_path=None):
        if parent_path and not parent_path.startswith("<") and 
            not parent_path.startswith("/") and 
                not name.startswith("/"):
            current_path = os.path.join(self.root, parent_path)
            file_dir = os.path.dirname(os.path.abspath(current_path))
            relative_path = os.path.abspath(os.path.join(file_dir, name))
            if relative_path.startswith(self.root):
                name = relative_path[len(self.root) + 1:]
        return name

             就是讲各种路径合并,然后得到模板的绝对路径,最后去除根路径,返回剩余的路径。

    • _create_template方法
    def _create_template(self, name):
        path = os.path.join(self.root, name)
        with open(path, "rb") as f:
            template = Template(f.read(), name=name, loader=self)
            return template

            根据模板的绝对路径,打开模板文件,然后实例化Template对象并返回。实例化的Template对象会缓存在加载器中。

  • 相关阅读:
    类的加载
    java经典面试题(转)
    I/O NIO 2
    【转】Impala和Hive的关系
    【转】工作站和服务器的区别
    JAVA之线程
    【转】Linux中vim的粘贴复制快捷键的使用
    【转】Zookeeper集群为什么要是单数
    【转】Impala常见错误
    hadoop命令工作常用
  • 原文地址:https://www.cnblogs.com/liaofeifight/p/4962216.html
Copyright © 2011-2022 走看看