flask-cache 快取Jinja2模板之原始碼解讀
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。這樣可以區分不同包下相同類的識別符號唯一的情況。