flask-cache 缓存Jinja2模板之源码解读

时间:2022-08-20 20:29:24

注:缓存jinja2模板的代码详见flask-cache缓存的示例代码

1.缓存模板的关键代码:

模板cached_blueprint_app/templates/zen.html
定义缓存参数:

{% cache timeout %}   #timeout是缓存超期时间
{% endcache %} # 缓存区域结束标记

文件cached_blueprint_app/app.py中实例一个当前应用的缓存:

app.cache = Cache(app)

2.源码解读

2.1 Cache类初始化的关键代码如下:

class Cache:
def __init__(self,app=None,with_jinja2_ext=True,config=None):
if app is not None:
self.init_app(app, config) #<1>

def init_app(self, app, config=None):
if self.with_jinja2_ext:
from .jinja2ext import CacheExtension, JINJA_CACHE_ATTR_NAME #<2>
setattr(app.jinja_env, JINJA_CACHE_ATTR_NAME, self) #<3>
app.jinja_env.add_extension(CacheExtension) #<4>

def add_extension(self, extension): #<5>
self.extensions.update(load_extensions(self, [extension])) #<6>

上边的代码说明:
<1>初始化当前app缓存操作.
<2>CacheExtension jinja2模板的缓存扩展,实现模板变量缓存的核心功能.
<3>将cache实例存储到jinja_env属性中,方便后边存储缓存时,从环境中取出cache对象.
<4>将CacheExtension加入到jinja环境的扩展中.
<5>当环境创建之后,加入扩展
<6>使用load_extensions的返回结果,来更新扩展程序的字典,实现注册缓存实例到特定环境的功能

2.2 load_extension函数实现CacheExtension的实例化

def load_extensions(environment, extensions):           #<7>
result[extension.identifier]=extension(environment) #<8>

<7>实现从扩展列表中加载扩展,并与当前的环境绑定,返回app环境的实例(一个字典形式)
<8>result 是返回值,是以扩展的标识符为key,以扩展实例为value的字典形式
<8> 扩展标识符是扩展在注册时生成的(ExtensionRegistry类__new__方法实现)

2.3 缓存核心类CacheExtension鸟瞰

UML图如下:
flask-cache 缓存Jinja2模板之源码解读

说明:
1) CacheExtension的父类Extension实现扩展的基本接口的定义。
2) Extension的父类ExtensionRegistry 实现扩展注册分配标识符的功能。

CacheExtension的功能很简单简单主要有一个字段和两个方法组成:
(1) 字段tags=set([‘cache’])
用于解析匹配,当检测到模板中的tag与CacheExtension的tag字段匹配时,表示当前模板文件有变量需要缓存。
(2) 方法CacheExtension._cache
进行实际的解析操作。当检测到实际的缓存结束的关键字endcache时,会获取整个模板文件的主要内容(body),随后调用内部方法_cache将变量存入缓存中。

    def _cache(self, timeout, fragment_name, vary_on, caller):
try:
cache = getattr(self.environment, JINJA_CACHE_ATTR_NAME) #<9>
except AttributeError as e:
raise e

key = make_template_fragment_key(fragment_name, vary_on=vary_on) #<10>
rv = cache.get(key)
if rv is None:
rv = caller()
cache.set(key, rv, timeout) #<11>
return rv
def make_template_fragment_key(fragment_name, vary_on=[]):
"""
Make a cache key for a specific fragment name
"""

if vary_on:
fragment_name = "%s_" % fragment_name
return TEMPLATE_FRAGMENT_KEY_TEMPLATE % (fragment_name, "_".join(vary_on)) #<10>

<9> 从jinja环境中取出cache对象
<10> 生成模板的键,键的格式是前缀+完整路径名的形式(如果vary_on不存在)
<11> 将模板中的值存入缓存

附注一个示例的body内容:

Body = [Output(nodes=[TemplateData(data='\n<h3> Random Zen of Python </h3>\n<strong>'),
Call(node=Name(name='get_random_quote', ctx='load'), args=[],
TemplateData(data='</strong>\n')])]

解释一下:
TemplateData里边模板的具体内容。
Call里边是模板调用的方法get_random_quote
ExtensionRegistry 类的代码很简单,只有一个创建对象的方法。实现了两个功能:
1创建类2.为类创建唯一的表示符

class ExtensionRegistry(type):
"""Gives the extension an unique identifier."""

def __new__(cls, name, bases, d):
rv = type.__new__(cls, name, bases, d)
rv.identifier = rv.__module__ + '.' + rv.__name__ #标识符采用包名+'.'+类名
return rv

在对jinja缓存时,CacheExtension的扩展标识符(identifier)是:flask_cache.jinja2ext.CacheExtension。这样可以区分不同包下相同类的标识符唯一的情况。

3.缓存模板的完整函数调用图:

flask-cache 缓存Jinja2模板之源码解读