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

如何Hook变量引用自动执行代码,实现Python映射数据每次访问自动刷新

解决方案

你可以通过伪装成普通字典的动态映射对象实现需求,仅需要修改models.py中原有映射的定义代码,所有引用映射的业务代码完全不需要改动。

核心思路是自定义类模拟字典的全部访问行为,在访问时自动触发数据库查询拉取最新数据,对外暴露的调用接口和原生字典完全一致。


实现方案1:每次访问实时查库(强一致性)

适合映射对应的表数据量不大(比如代理商表记录数低于1万条)的场景,能保证每次拿到的都是数据库最新数据,兼容跨实例修改、同实例新增数据的实时同步需求。

首先在models.py中替换原有映射的定义:

import db
from sqlalchemy.orm.exc import NoResultFound

class DynamicIdMap:
    def __init__(self, model_cls, key_col="name", value_col="id"):
        self.model_cls = model_cls
        self.key_col = key_col
        self.value_col = value_col

    # 兼容字典[]取值语法
    def __getitem__(self, key):
        try:
            return db.session.query(getattr(self.model_cls, self.value_col)).filter(
                getattr(self.model_cls, self.key_col) == key
            ).scalar()
        except NoResultFound:
            raise KeyError(key)

    # 兼容字典get方法
    def get(self, key, default=None):
        try:
            return self[key]
        except KeyError:
            return default

    # 兼容字典in判断
    def __contains__(self, key):
        return db.session.query(
            db.session.query(self.model_cls).filter(
                getattr(self.model_cls, self.key_col) == key
            ).exists()
        ).scalar()

    # 兼容字典keys()/items()方法,用于填充下拉框等场景
    def keys(self):
        return [k for k, _ in self.items()]

    def items(self):
        return db.session.query(
            getattr(self.model_cls, self.key_col),
            getattr(self.model_cls, self.value_col)
        ).all()

# 替换原有agents_id_map的定义,不需要修改任何其他业务代码
agents_id_map = DynamicIdMap(db.Agent)
# 其他映射可以同理快速定义
# customer_id_map = DynamicIdMap(db.Customer)
# product_id_map = DynamicIdMap(db.Product)

实现方案2:带缓存的动态映射(兼顾性能和一致性)

如果表数据量较大,每次访问都查库会有性能损耗,可以增加本地缓存和过期机制,同时支持主动刷新缓存:

import time
import db

class CachedDynamicIdMap:
    def __init__(self, model_cls, key_col="name", value_col="id", cache_ttl=30):
        self.model_cls = model_cls
        self.key_col = key_col
        self.value_col = value_col
        self.cache_ttl = cache_ttl  # 缓存有效期,单位秒,设为0则每次都查库
        self._cache = None
        self._cache_update_time = 0

    def _refresh_cache(self):
        # 缓存过期或为空时重新拉取全量数据
        now = time.time()
        if self._cache is None or now - self._cache_update_time > self.cache_ttl:
            # 拉取前清空会话一级缓存,确保拿到最新数据
            db.session.expire_all()
            self._cache = {
                k: v for k, v in db.session.query(
                    getattr(self.model_cls, self.key_col),
                    getattr(self.model_cls, self.value_col)
                ).all()
            }
            self._cache_update_time = now

    def __getitem__(self, key):
        self._refresh_cache()
        return self._cache[key]

    def get(self, key, default=None):
        self._refresh_cache()
        return self._cache.get(key, default)

    def __contains__(self, key):
        self._refresh_cache()
        return key in self._cache

    def items(self):
        self._refresh_cache()
        return self._cache.items()

    # 主动刷新缓存的接口,同实例新增/修改/删除数据后调用可立刻同步
    def flush(self):
        self._cache = None
        self._cache_update_time = 0

# 定义映射
agents_id_map = CachedDynamicIdMap(db.Agent, cache_ttl=60)

同实例内新增代理商后,只需要调用agents_id_map.flush()即可立刻刷新缓存,不需要等待过期时间。


注意事项

  • 确保SQLAlchemy会话的expire_on_commit参数保持默认的True,避免会话返回旧缓存数据
  • 如果对跨实例数据同步的实时性要求极高,建议将cache_ttl设为5秒以内,或者用数据库通知机制触发缓存刷新

内容的提问来源于stack exchange,提问作者user12181582

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 19:48:03