Flask-caching 的快取與刪除 —— 避坑指南

songofhawk發表於2023-04-12

基本用法

基於 Flask 應用,難免會用到 Flask-cache (或 Flask-cacheing,兩者API 基本相同)。它透過裝飾器,非常優雅地實現了函式呼叫的快取。裝飾器可以直接加在 view function ,或者普通 function 上,類似下面的樣子:

@app.route('/demo/student/all', methods=['GET'])
@cache.cached(timeout=600)
def get():
    students = Student.query.all()
    return response_ok(students)

(如果函式有引數,並且希望針對不同實參值,做不同的快取,那就用 _@cache.memerized _裝飾器)
新增了裝飾器的函式,在第一次被呼叫之後,返回值就被在快取有效期內,示例中的timeout 引數,保證了快取在600秒後超時失效,再次呼叫函式本體獲得最新資料。

view function 快取控制

但有時我們需要控制這個快取的更新——比如上面的例子中,每次新增一個學生、或者更改學生資料,就重新重新整理一下快取,而不需要等它自然超時。這時就需要顯式呼叫 delete 函式,例如:

del_res = cache.delete('/demo/student/all')

對於 view function 來說,這裡 delete 的引數,就恰好等於 route 裡的 path,如果 route 來自於一個上級的 blueprint,要記得把上級 path 帶上。
如果你要指定 cache 的 key,可以加上 key_prefix 引數,類似下面這樣:

@app.route('/demo/student/all', methods=['GET'])
@cache.cached(timeout=600, key_prefix='res_student_all')
def get():
    ...

注意第一個坑來了:因為名字裡有個 prefix,你可能會覺得它是 key 的字首,刪除的時候,會想著怎麼組合一下這個引數和 path;然而實際上卻不用任何組合,path 在這裡已經無效了,刪除時直接用這個 key_prefix 當引數就可以:

del_res = cache.delete('res_student_all')

事實上,這個 key_prefix 既不是 key 的字首,也不是整個 key,它應該叫 key 的字尾更合理,真正儲存的 key(存在那裡取決於配置,可以有記憶體快取SimpleCache、redis快取等等)字首是這個樣子的:**flask_cache_**,而無論是 path 還是 key_prefix,都會在 hash 之後拼接在後面。

普通 function 快取控制

簡單函式

如果是普通的 function,要使用 @cache.memoize 裝飾器,同樣支援 timeout 引數,如下:

@cache.memoize(timeout=60)
def get_number():
    return 5

刪除 memoize 要更直觀一些,直接傳函式就可以啦:

del_res = cache.delete_memoize(get_number)

如果該函式有引數:

@cache.memoize(timeout=60)
def get_number(x):
    return 5 + x

刪除時加引數,表示只刪除這個引數對應的快取,不加引數,表示刪除該函式的所有實參值對應快取:

del_res = cache.delete_memoize(get_number, 5)    # 只刪除x=5的快取
del_res = cache.delete_memoize(get_number)    # 刪除所有 get_number 快取

類函式

這時第二個坑來了,如果這是個類函式呢?

@cache.memoize(timeout=60)
class Calc:
    @classmethod
    def get_number(x):
        return 5 + x

刪除時,只傳函式名,或者函式名加引數,是刪不掉的:

del_res = cache.delete_memoize(Calc.get_number, 5)    # 無法刪除x=5的快取
del_res = cache.delete_memoize(Calc.get_number)    # 無法刪除所有 get_number 快取

官方文件說,“要把類當做第一個引數傳進來”,這個的表述非常容易誤解,實際上應該是引數列表裡的第一個,所以要像下面這樣傳:

del_res = cache.delete_memoize(Calc.get_number, Calc, 5)    # 刪除x=5的快取
del_res = cache.delete_memoize(Calc.get_number, Calc)    # 刪除所有 get_number 快取

相關文章