Flask模板引用原理机制

Flask使用的是Jinja模板,除此之外还有模板还有Flask-Mako,Flask-Genshi,但不常用。

Jinja有两种定界符。{% ... %}和{{ ... }}。前者用于执行像for循环或赋值等语句,后者向模板输出一个表达式的结果。

使用 extends 和 block实现模板的继承。

在Jinja2模板中可以使用条件语句(if else endif),for循环( for ..endfor) ,计算(+ - * /),逻辑与,或等等。Jinja2本身的知识点很多,可以参考: Jinja2文档

本文主要研究引用模板的原理.首先是调用模板的函数:

def render_template(template_name_or_list, **context): """Renders a template from the template folder with the given context.

:param template_name_or_list: the name of the template to be
                              rendered, or an iterable with template names
                              the first one existing will be rendered
:param context: the variables that should be available in the
                context of the template.
"""
ctx = _app_ctx_stack.top
ctx.app.update_template_context(context)
return _render(ctx.app.jinja_env.get_or_select_template(template_name_or_list),
               context, ctx.app)

首先会更新模板要引用的上下文,将模板中的变量加入到模板上下文中。该函数是引用模板的重点函数,再往下看,ctx.app.jinja_env:


@locked_cached_property
def jinja_env(self):
    """The Jinja2 environment used to load templates."""
    return self.create_jinja_environment()

def create_jinja_environment(self): """Creates the Jinja2 environment based on :attr:jinja_options and :meth:select_jinja_autoescape. """ options = dict(self.jinja_options) if 'autoescape' not in options: options['autoescape'] = self.select_jinja_autoescape rv = Environment(self, **options) rv.globals.update( url_for=url_for, get_flashed_messages=get_flashed_messages, config=self.config, # request, session and g are normally added with the # context processor for efficiency reasons but for imported # templates we also want the proxies in there. request=request, session=session, g=g ) rv.filters['tojson'] = json.tojson_filter return rv

上述函数将request,session,g等也加入到上下文中,以便在模板中调用。上述函数返回的是一个Environment类,该类的重要作用是正确找到模板的位置。那么就要搞清到底如何正取找到模板位置(一般在import_name相同的目录中的templates中)。

而templates位置,我们在APP实例化的时候就已经定义,默认:


class Flask:
    def init(self, import_name, static_path=None, static_url_path=None,
             static_folder='static', template_folder='templates',
             instance_path=None, instance_relative_config=False):
        _PackageBoundObject.init(self, import_name,
                                         template_folder=template_folder)

class _PackageBoundObject(object):

def __init__(self, import_name, template_folder=None):
    #: The name of the package or module.  Do not change this once
    #: it was set by the constructor.
    self.import_name = import_name

    #: location of the templates.  `None` if templates should not be
    #: exposed.
    self.template_folder = template_folder

    #: Where is the app root located?
    self.root_path = get_root_path(self.import_name)

在上述代码中指定了template文件夹的位置。而到底如何找到指定文件夹的模板文件的呢,这时候就要用到上面提到的Environment了。Enviroment寻找指定目标文件的方法是通过加载器loader实现的。在Flask中,定义了DispatchingJinjaLoader加载器:

class Environment(BaseEnvironment):
    """Works like a regular Jinja2 environment but has some additional
    knowledge of how Flask's blueprint works so that it can prepend the
    name of the blueprint to referenced templates if necessary.
    """

def __init__(self, app, **options):
    if 'loader' not in options:
        options['loader'] = app.create_global_jinja_loader()
    BaseEnvironment.__init__(self, **options)
    self.app = app

create_global_jinja_loader :return DispatchingJinjaLoader,初始化BaseEnviroment时会加上该加载器,该环境类会通过该加载器寻找到模板文件。

加载器关键的一步:

def jinja_loader(self):
    """The Jinja loader for this package bound object.

    .. versionadded:: 0.5
    """
    if self.template_folder is not None:
        return FileSystemLoader(os.path.join(self.root_path,
                                             self.template_folder))

通过FileSystemLoader就可以找到模板文件。到此为止,大功告成!

下面把静态文件也简单说一下:

静态文件:


if self.has_static_folder:
    self.add_url_rule(self.static_url_path + '/',
                      endpoint='static',
                      view_func=self.send_static_file)
静态文件可以像访问URL一样进行访问。/static/filename

--------EOF---------
微信分享/微信扫码阅读