1. 程式人生 > >flask-cache 快取Jinja2模板之原始碼解讀

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。這樣可以區分不同包下相同類的識別符號唯一的情況。

3.快取模板的完整函式呼叫圖:

這裡寫圖片描述