Flask-Cache缓存SQLAlchemy分页结果或带Cookie视图的方案咨询
问题解答
一、能否用Flask-Cache缓存SQLAlchemy分页查询结果?
当然可以,但不能直接缓存SQLAlchemy的模型实例或包含db.session的查询对象——因为pickle序列化无法处理带有数据库会话引用的对象。你需要做的是将查询结果转换为纯数据结构(比如字典、列表)后再缓存,这样就能避开序列化问题。
举个例子,修改你原来的查询函数:
from your_app.models import Job @cache.memoize(60 * 60) def find_job_per_id(job_id): job = Job.query.filter(Job.id == job_id).first() if not job: return None # 把模型实例转为纯字典,只保留需要的字段 return { "id": job.id, "title": job.title, "description": job.description, "created_at": job.created_at.isoformat() # 日期类型要转成字符串才能序列化 # 其他你需要的字段 }
如果是分页查询,同样处理分页结果的元数据和条目:
@cache.memoize(60 * 60) def get_paginated_jobs(page, per_page, search_keyword=None): query = Job.query if search_keyword: query = query.filter(Job.title.contains(search_keyword)) pagination = query.paginate(page=page, per_page=per_page) # 序列化分页数据 return { "items": [ {"id": job.id, "title": job.title, "description": job.description} for job in pagination.items ], "page": pagination.page, "per_page": pagination.per_page, "total": pagination.total, "pages": pagination.pages }
这样返回的纯数据结构可以被pickle正常序列化,缓存就没问题了。如果字段多,也可以用marshmallow这类序列化库来简化模型转字典的过程,更高效。
二、如何实现基于Cookie维度的视图缓存?
核心思路是让缓存键包含用户Cookie的唯一标识,这样不同用户(不同Cookie值)会生成不同的缓存键,避免缓存串用。Flask-Cache允许你自定义缓存键的生成逻辑,具体步骤如下:
- 定义一个自定义缓存键生成函数,把目标Cookie值(比如存储用户最新搜索记录的
latest_search)加入到缓存键中:
import hashlib from flask import request, current_app def get_cache_key_with_cookie(): # 获取用户Cookie中的搜索记录(如果没有则用空字符串) latest_search = request.cookies.get("latest_search", "") # 对Cookie值做哈希处理,避免键太长(可选,但推荐) search_hash = hashlib.md5(latest_search.encode("utf-8")).hexdigest() # 获取默认的缓存键(基于URL、参数等) base_key = current_app.extensions["cache"].make_cache_key() # 拼接成最终的缓存键 return f"{base_key}_{search_hash}"
- 在视图装饰器中使用这个自定义键生成函数:
@app.route("/search") @cache.cached(timeout=60*60, key_prefix=get_cache_key_with_cookie) def search_view(): # 从Cookie获取搜索记录 latest_search = request.cookies.get("latest_search", "") # 获取分页参数 page = request.args.get("page", 1, type=int) # 调用缓存后的分页查询函数 jobs_data = get_paginated_jobs(page, 10, latest_search) # 渲染模板 return render_template( "search.html", jobs=jobs_data["items"], pagination=jobs_data )
这样,每个用户的latest_search Cookie值不同,生成的缓存键就不同,各自的缓存内容完全独立,不会出现用户B拿到用户A缓存的问题。
另外,如果你更倾向于用@cache.memoize,也可以把Cookie值作为参数传入被缓存的函数——memoize会根据参数生成唯一缓存键:
@app.route("/search") def search_view(): latest_search = request.cookies.get("latest_search", "") page = request.args.get("page", 1, type=int) # 调用被memoize缓存的函数,传入Cookie值作为参数 return _render_search_page(latest_search, page) @cache.memoize(60*60) def _render_search_page(latest_search, page): jobs_data = get_paginated_jobs(page, 10, latest_search) return render_template( "search.html", jobs=jobs_data["items"], pagination=jobs_data )
这种方式更简洁,适合逻辑简单的视图,但要注意:如果模板中还有其他用户特定的内容(比如用户名),也要把这些内容作为参数传入_render_search_page,否则缓存会串用。
内容的提问来源于stack exchange,提问作者Laurent Meyer
相关产品推荐
相关产品推荐

