使用 Flask-Caching 缓存数据
简介
Flask-Caching 是 Flask 的一个扩展,为任何 Flask 应用程序添加了对各种后端的缓存支持。它基于 cachelib 运行,并通过统一的 API 支持 werkzeug 的所有原始缓存后端。开发者还可以通过继承 flask_caching.backends.base.BaseCache 类来开发自己的缓存后端。
官方文档 - https://flask-caching.readthedocs.io/en/latest/index.html
安装
pip install Flask-Caching
设置
缓存通过缓存实例来管理
from flask import Flask
from flask_caching import Cache
config = {
"DEBUG": True, # some Flask specific configs
"CACHE_TYPE": "SimpleCache", # Flask-Caching related configs
"CACHE_DEFAULT_TIMEOUT": 300
}
app = Flask(__name__)
# tell Flask to use the above defined config
app.config.from_mapping(config)
cache = Cache(app)
也可以使用init_app来延后配置缓存实例
cache = Cache(config={'CACHE_TYPE': 'SimpleCache'})
app = Flask(__name__)
cache.init_app(app)
还可以提供一个备用的配置字典,如果有多个Cache缓存实例,每个实例使用不同的后端,这将非常有用。
#: Method A: During instantiation of class
cache = Cache(config={'CACHE_TYPE': 'SimpleCache'})
#: Method B: During init_app call
cache.init_app(app, config={'CACHE_TYPE': 'SimpleCache'})
缓存视图函数
使用cached()装饰器缓存视图函数,默认使用path作为缓存的key
@app.route("/")
@cache.cached(timeout=50)
def index():
return render_template('index.html')
cached 装饰器还有另一个可选参数叫做 unless。这个参数接受一个可调用对象,它返回 True 或 False。如果 unless 返回 True,那么将完全跳过缓存机制。
为了在视图中动态确定超时时间,可以返回 CachedResponse,这是 flask.Response 的子类。
@app.route("/")
@cache.cached()
def index():
return CachedResponse(
response=make_response(render_template('index.html')),
timeout=50,
)
缓存插拔式视图类
from flask.views import View
class MyView(View):
@cache.cached(timeout=50)
def dispatch_request(self):
return 'Cached for 50s'
缓存其它函数
使用相同的 @cached 装饰器,还可以缓存其他非视图相关的函数的结果。需要注意替换 key_prefix,否则它将使用 request.path 作为 cache_key。键控制从缓存中获取什么内容。例如,如果一个键在缓存中不存在,将会在缓存中创建一个新的键值对条目。否则,将会返回该键的值(即缓存的结果)。
@cache.cached(timeout=50, key_prefix='all_comments')
def get_all_comments():
comments = do_serious_dbio()
return [x.author for x in comments]
cached_comments = get_all_comments()
自定义缓存键
有时您希望为每个路由定义自己的缓存键。使用 @cached 装饰器,您可以指定如何生成这个键。当缓存键不应仅仅是默认的 key_prefix,而是必须从请求中的其他参数派生时,这可能会非常有用。例如,在缓存 POST 路由时,缓存键应该根据请求中的数据而不仅仅是路由或视图本身来确定,这时就可以使用这个功能。
def make_key():
"""A function which is called to derive the key for a computed value.
The key in this case is the concat value of all the json request
parameters. Other strategy could to use any hashing function.
:returns: unique string for which the value should be cached.
"""
user_data = request.get_json()
return ",".join([f"{key}={value}" for key, value in user_data.items()])
@app.route("/hello", methods=["POST"])
@cache.cached(timeout=60, make_cache_key=make_key)
def some_func():
return "hello"
记忆化
在记忆化中,函数参数也会包含在cache_key中
注意:对于不接收参数的函数来说,cached() 和 memoize() 实际上是相同的。
Memoize 也适用于方法,因为它会将 self或 cls 参数的身份作为缓存键的一部分。
记忆化背后的理论是,如果你有一个函数需要在一次请求中多次调用,那么它只会在第一次使用这些参数调用该函数时进行计算。例如,一个 sqlalchemy 对象用来确定一个用户是否具有某个角色。在一次请求中,你可能需要多次调用这个函数。为了避免每次需要这些信息时都访问数据库,你可能会做如下操作:
class Person(db.Model):
@cache.memoize(50)
def has_membership(self, role_id):
return Group.query.filter_by(user=self, role_id=role_id).count() >= 1
将可变对象(类等)作为缓存键的一部分可能会变得棘手。建议不要将对象实例传递给记忆化函数。然而,memoize 会对传入的参数执行 repr(),因此如果对象有一个返回唯一标识字符串的 __repr__ 函数,该字符串将被用作缓存键的一部分。
例如,一个 sqlalchemy 的 person 对象,返回数据库 ID 作为唯一标识符的一部分:
class Person(db.Model):
def __repr__(self):
return "%s(%s)" % (self.__class__.__name__, self.id)