注:緩存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圖如下:
說明:
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。這樣可以區分不同包下相同類的標識符唯一的情況。