Триггер в sqlachemy

у меня есть две таблицы, связанные через внешний ключ, здесь они используют декларативное отображение

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)

Итак, я хотел бы обновить соответствующие задачи.состояние, когда обс.состояние изменяется на значение 2. В настоящее время я делаю это вручную (используя отношение под названием task)

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. Я читал вставить обновить значения по умолчанию несколько раз, но не могу найти путь. Я даже не знаю, возможно ли это.

1 ответов


Вы можете создать триггер в базе данных класс 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)

Это самый надежный способ: он будет работать для массовых обновлений, когда 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, когда задача обновляется. Вы должны добавить еще один триггер или обработчик событий для поддержки распространения изменений в обоих направлениях.