模板文件是怎么被找到、读进来、变成 Python 代码的?Flask 的加载器和 Jinja 的有什么关系?能不能换成数据库或字典?这篇从加载器的接口讲起,一直打印到编译出来的 Python 源码。
app.jinja_env 类型: flask.templating.Environment | loader: DispatchingJinjaLoader
app.jinja_loader: FileSystemLoader | searchpath: ['<项目>/templates']
Flask 3.1 对 jinja_env 的默认选项:autoescape = method | auto_reload = False | cache_size = 400 | trim_blocks = False | lstrip_blocks = False | undefined = Undefined
jinja_options: {}上一篇:Content Negotiation;下一篇:Flask 模板搜索路径。
源码 = 'Hello {{ name }}!\n' | 文件名 = <项目>/templates/hello.html | uptodate() = True
文件被修改后 uptodate() = False (返回 False 表示缓存的编译结果过期了)
找不到 → TemplateNotFound: nope.htmlDictLoader: A:1 ChoiceLoader:a.html → 覆盖的 A | hello.html → Hello x! PrefixLoader:ui/btn.html → <button> | mail/btn.html → 邮件按钮 FunctionLoader:dyn/x → 来自函数:dyn/x | 调用记录 ['dyn/x']
GET /h → 被覆盖:ann | GET /p → <html><p>ann</p></html>
from jinja2.runtime import LoopContext, Macro, Markup, Namespace, TemplateNotFound, TemplateReference, TemplateRuntimeError, Undefined, escape, identity, internalcode, markup_join, missing, str_join
name = None
def root(context, missing=missing, environment=environment):
resolve = context.resolve_or_missing
undefined = environment.undefined
concat = environment.concat
cond_expr_undefined = Undefined
if 0: yield None
l_0_name = resolve('name')
l_0_items = resolve('items')
try:
t_1 = environment.filters['upper']
except KeyError:
@internalcode
def t_1(*unused):
raise TemplateRuntimeError("No filter named 'upper' found.")
pass
yield 'Hello '
yield escape((undefined(name='name') if l_0_name is missing else l_0_name))
yield '!'
for l_1_i in (undefined(name='items') if l_0_items is missing else l_0_items):
_loop_vars = {}parse(只生成语法树)2.77 ms;compile(含代码生成与 Python 编译)8.36 ms;已编译后 render 0.044 ms;编译是渲染的 191 倍 —— 所以缓存编译结果很重要