flask-cache 緩存Jinja2模板之源碼解讀

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

3.緩存模板的完整函數調用圖:

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章