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实例化的时候就已经定义,默认:
在上述代码中指定了template文件夹的位置。而到底如何找到指定文件夹的模板文件的呢,这时候就要用到上面提到的Environment了。Enviroment寻找指定目标文件的方法是通过加载器loader实现的。在Flask中,定义了DispatchingJinjaLoader加载器: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)
create_global_jinja_loader :return DispatchingJinjaLoader,初始化BaseEnviroment时会加上该加载器,该环境类会通过该加载器寻找到模板文件。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
加载器关键的一步:
通过FileSystemLoader就可以找到模板文件。到此为止,大功告成!
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))
下面把静态文件也简单说一下:
静态文件:
if self.has_static_folder:
self.add_url_rule(self.static_url_path + '/',
endpoint='static',
view_func=self.send_static_file)
静态文件可以像访问URL一样进行访问。/static/filename
微信分享/微信扫码阅读