SQLalchemy: предварительное удаление UPDATE не запускается, когда post_update = True - PullRequest
0 голосов
/ 15 мая 2018

У меня есть объектная модель, которая использует систему «действий» для отслеживания изменений в объекте. Эти действия могут быть добавлены и считаются «предложенными» до тех пор, пока они не будут утверждены с помощью действия «одобрить». Полный список действий сокращен до «состояний» объектов, которые также хранятся в базе данных, для более быстрого запроса.

Поскольку существуют «предлагаемые» действия, у каждого объекта также есть второй объект состояния, который отслеживает «diff», которые в основном являются изменениями состояния для всего в предлагаемом (все остальные поля установлены в NULL). Наконец, каждое состояние также отслеживает последнее действие для отображения «последнего обновления» в пользовательском интерфейсе.

Вот мой объект:

class Subject(Base):
    __tablename__ = 'subjects'

    id = Column(UUID(as_uuid=True), primary_key=True)
    state_id = Column(UUID(as_uuid=True), ForeignKey('states.id', name="fk_state"))
    diff_id = Column(UUID(as_uuid=True), ForeignKey('states.id', name="fk_state_diff"))

    state = relationship('State', back_populates='subject', foreign_keys=[state_id], cascade='all, delete-orphan', lazy='joined', innerjoin=True, uselist=False, single_parent=True, post_update=True)
    diff = relationship('State', back_populates='subject', foreign_keys=[diff_id], cascade='all, delete-orphan', lazy='joined', innerjoin=True, uselist=False, single_parent=True, post_update=True)
    actions = relationship('Action', back_populates='subject', cascade='all, delete-orphan', order_by='Action.time', single_parent=True)

Как видите, он имеет отношение к этому объекту Action:

class Action(Base):
    __tablename__ = 'actions'

    id = Column(UUID(as_uuid=True), primary_key=True)
    subject_id = Column(UUID(as_uuid=True), ForeignKey('subjects.id'), nullable=False, index=True)

    time = Column(DateTime(timezone=True), server_default=func.now(), nullable=False, index=True)
    # Other action properties here, omitted for brevity.
    # ...

    subject = relationship('Subject', back_populates='actions')

и оба они относятся к объекту State с его предлагаемыми (многие-ко-многим) и свойствами last_action.

proposed_action_data = Table(
    'proposed_actions',
    Base.metadata,
    Column('actions_id', UUID(as_uuid=True), ForeignKey('actions.id')),
    Column('state_id', UUID(as_uuid=True), ForeignKey('states.id'))
)

class State(Base):
    __tablename__ = 'states'

    id = Column(UUID(as_uuid=True), primary_key=True)
    last_action_id = Column(UUID(as_uuid=True), ForeignKey('actions.id', name="fk_state_last_action"), nullable=True)
    subject_id = Column(UUID(as_uuid=True), ForeignKey('subjects.id', name="fk_state_subject_id"), nullable=False)

    # State properties goes here, ommitted for brevity.
    # ...

    last_action = relationship('Action', lazy='joined', foreign_keys=[last_action_id], post_update=True)
    proposed = relationship('Action', secondary=proposed_action_data)
    subject = relationship('Subject', foreign_keys=[subject_id)

Вставки, кажется, работают просто отлично - я могу установить состояние и разность, и оператор UPDATE выдается для субъекта после оператора INSERT, поэтому идентификаторы установлены.

Но если я хочу удалить Subject, оператор pre-delete UPDATE, чтобы очистить state_id и diff_id, не запускается, поэтому он пытается удалить State и завершается ошибкой, поскольку на него все еще ссылаются:

session.delete(subject)
session.commit()

поднимает:

sqlalchemy.exc.IntegrityError: (psycopg2.IntegrityError) update or delete on table "states" violates foreign key constraint "fk_state_diff" on table "subjects"
DETAIL:  Key (id)=(363eb5bb-4042-44a9-b3fe-567406008f9e) is still referenced from table "subjects".
[SQL: 'DELETE FROM states WHERE states.id = %(id)s'] [parameters: ({'id': UUID('363eb5bb-4042-44a9-b3fe-567406008f9e')}, {'id': UUID('b7ca4f02-0fac-41bf-ae8b-44049ceb1855')})]

Если я очищаю состояние и diff вручную, я не получаю никаких исключений:

subject.state = None
subject.diff = None
session.delete(subject)
session.commit()

Что я здесь не так делаю?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...