触发sqlachemy

Ser*_*gio 14 python triggers sqlalchemy

我有两个通过外键相关的表,这里他们使用声明映射

class Task(DeclarativeBase):
    __tablename__ = 'task'
    id = Column(Integer, primary_key=True)
    state = Column(Integer, default=0)
    obs_id = Column(Integer, ForeignKey('obs.id'), nullable=False)

class Obs(DeclarativeBase):
    __tablename__ = 'obs'
    id = Column(Integer, primary_key=True)
    state = Column(Integer, default=0)
Run Code Online (Sandbox Code Playgroud)

因此,当obs.state更改为值2时,我想更新相关的task.state.目前我正在手动执行(使用称为task的关系)

obs.state = 2
obs.task.state = 2
Run Code Online (Sandbox Code Playgroud)

但我更喜欢使用触发器.我已经检查过这可以在sqlite中使用

CREATE TRIGGER update_task_state UPDATE OF state ON obs
  BEGIN
    UPDATE task SET state = 2 WHERE (obs_id = old.id) and (new.state = 2);
  END;
Run Code Online (Sandbox Code Playgroud)

但我找不到如何在sqlalchemy中表达这一点.我已多次读取插入更新默认值,但无法找到方法.我不知道是否有可能.

Den*_*ach 20

您可以使用DDL类在数据库中创建触发器:

update_task_state = DDL('''\
CREATE TRIGGER update_task_state UPDATE OF state ON obs
  BEGIN
    UPDATE task SET state = 2 WHERE (obs_id = old.id) and (new.state = 2);
  END;''')
event.listen(Obs.__table__, 'after_create', update_task_state)
Run Code Online (Sandbox Code Playgroud)

这是最可靠的方法:当不使用ORM时,它将适用于批量更新,甚至适用于应用程序之外的更新.但也有缺点:

  • 你必须小心你的触发器存在和最新;
  • 它不可移植,因此如果更改数据库,则必须重写它;
  • SQLAlchemy不会更改已加载对象的新状态,除非它过期(例如,使用某个事件处理程序).

下面的可靠性较低(仅在ORM级别进行更改时才会起作用),但更简单的解决方案:

from sqlalchemy.orm import validates

class Obs(DeclarativeBase):
    __tablename__ = 'obs'
    id = Column(Integer, primary_key=True)
    state = Column(Integer, default=0)
    @validates('state')
    def update_state(self, key, value):
        self.task.state = value
        return value
Run Code Online (Sandbox Code Playgroud)

我的两个示例都以一种方式工作,即在更改obs时更新任务,但在更新任务时不触摸obs.您必须再添加一个触发器或事件处理程序以支持两个方向上的更改传播.