You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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允许你自定义缓存键的生成逻辑,具体步骤如下:

  1. 定义一个自定义缓存键生成函数,把目标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}"
  1. 在视图装饰器中使用这个自定义键生成函数:
@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:17:35