触发sqlalchemy

时间:2011-10-25 11:52:15

标签: 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)

所以,我想在obs.state更改为值2时更新相关的task.state。目前我正在手动执行(使用称为任务的关系)

obs.state = 2
obs.task.state = 2

但我更喜欢使用触发器。我已经检查过这适用于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;

但我找不到如何在sqlalchemy中表达这一点。我已多次阅读insert update defaults,但找不到方法。我不知道是否有可能。

1 个答案:

答案 0 :(得分:18)

您可以使用DDL class

在数据库中创建触发器
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)

这是最可靠的方法:当不使用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

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