SQLModel优化:如何简化__init__与update中自定义方法调用语法?
实现@run_on_change装饰器
要简化SQLModel实例创建/更新时的自定义逻辑调用,我们可以通过方法标记+自定义元类的方式实现目标装饰器,彻底消除样板代码。
1. 定义标记装饰器
先写一个轻量装饰器,给需要触发的方法打上标记:
def run_on_change(func): func._is_run_on_change = True return func
2. 自定义元类注入逻辑
继承SQLModel的元类,在类创建阶段收集标记方法,并重写__init__和update方法:
from sqlmodel import SQLModel from sqlmodel.main import SQLModelMeta class RunOnChangeMeta(SQLModelMeta): def __new__(cls, name, bases, namespace): # 收集所有标记了_run_on_change的方法 change_methods = [ method for _, method in namespace.items() if hasattr(method, "_is_run_on_change") ] # 重写__init__ original_init = namespace.get("__init__") def new_init(self, **kwargs): original_init(self, **kwargs) if original_init else super(self.__class__, self).__init__(**kwargs) for method in change_methods: method(self) # 重写update original_update = namespace.get("update") def new_update(self, **kwargs): original_update(self, **kwargs) if original_update else super(self.__class__, self).update(**kwargs) for method in change_methods: method(self) namespace["__init__"] = new_init namespace["update"] = new_update return super().__new__(cls, name, bases, namespace)
3. 创建通用基类
基于自定义元类创建基类,后续模型直接继承即可:
class BaseModel(SQLModel, metaclass=RunOnChangeMeta): pass
4. 模型中使用示例
现在可以用简洁的语法实现需求:
class MyModel(BaseModel, table=True): id: int | None = None name: str computed_field: str | None = None @run_on_change def custom_method(self): """创建或更新时自动执行的逻辑""" self.computed_field = self.name.upper()
测试验证:
# 创建实例触发逻辑 obj = MyModel(name="test") print(obj.computed_field) # 输出 TEST # 更新实例触发逻辑 obj.update(name="new test") print(obj.computed_field) # 输出 NEW TEST
是否用@listens_for替代?
SQLModel基于SQLAlchemy,@listens_for是SQLAlchemy的事件监听工具,和当前方案的核心差异在于触发时机:
- 自定义装饰器方案:作用于内存层面,只要调用
__init__或update就触发,不管是否涉及数据库操作。适合纯内存逻辑(如计算字段、内存数据验证)。 @listens_for方案:作用于数据库操作层面,仅在执行insert/update等数据库指令时触发(如before_insert/before_update事件)。适合和数据库强绑定的逻辑(如入库前数据校验、同步关联表数据)。
如果你的逻辑不需要和数据库操作绑定,自定义装饰器方案更直接;如果逻辑必须在数据库写入前执行,@listens_for是更合适的选择。
内容的提问来源于stack exchange,提问作者charrison
相关产品推荐
相关产品推荐

