2013-08-27 3 views
0

Попытка обернуть мою голову вокруг следующей проблемы:Остановить все на удаление каскадного SQLAlchemy

У меня есть три класса, A, AB и B, как это:

class AB(Base): 
    id = Column(Integer, primary_key=True) 
    a_id = Column(Integer, ForeignKey('a.id'), nullable=False) 
    a = relationship(
     'A', 
     cascade='save-update', 
     backref=backref(
      'abs', 
      cascade='save-update', 
      uselist=True 
     ) 
    ) 
    b_id = Column(Integer, ForeignKey('b.id'), nullable=False) 
    b = relationship(
     'B', 
     cascade='save-update', 
     backref=backref(
      'abs', 
      cascade='save-update', 
      uselist=True 
     ) 
    ) 
    __tablename__ = 'ab' 

class A(Base) 
    id = Column(Integer, primary_key=True) 
    __tablename__ = 'a' 

class B(Base) 
    id = Column(Integer, primary_key=True) 
    __tablename__ = 'b' 

По сути, это m2m между A и B. Единственной нестандартной вещью является столбец id в таблице AB. Это по какой-то причине.

Я хочу реализовать «слияние» двух экземпляров A. Мы даем a1 и a2. Затем, прежде чем удалять a1, все его отношения с AB s должны быть переназначены до a2. Крайне важно сохранить в этом процессе значения AB.id (следовательно, на самом деле не нужно создавать или удалять новые экземпляры AB).

ПРОБЛЕМА Независимо от того, как я стараюсь, каждый раз, когда я удалить экземпляр A, SQLAlchemy пытается обновить foreign_key со значением NULL, следовательно, нарушая NOT NULL ограничение. Это делается путем выдачи явного UPDATE ab SET a_id = NULL WHERE id = .... Он делает это, даже если у меня есть следующий цикл в моей программе:

for ab in a1.abs: 
    ab.a_id = a2.id 
    session.db.add(ab) 

session.db.delete(a1) 

Поэтому, казалось бы мне, что, прежде чем удалить выдается, все ab s связанные с a1 благополучно переехал в a2, однако что-то есть неправильно.

Некоторые не РЕШЕНИЕ

  • passive_deletes флага. Разница в поведении касается только тех строк, которые еще не находятся в памяти, что не очень хорошо.
  • Добавление каскада delete очень рискованно для меня. Я хочу действительно убедиться, что объекты ab теряются в процессе слияния.
  • Обновление списков, хранящихся в ассоциациях вручную, похоже, не имеет никакого эффекта (UPDATE выдается снова).

Был бы очень признателен за вашу помощь!

+0

Простое любопытство, вам удалось это решить? – Daren

+0

Да, фактически всего несколько часов назад. Как оказалось, SQLAlchemy, похоже, не обрабатывает корректные ручные обновления объектов, присутствующих в отношениях. Либо измените свои данные, используя только API связей; или вообще не полагайтесь на отношения при выполнении обновления. – julkiewicz

ответ

0

Хммм ... здесь очень плохо.

«UPDATE ab SET a_id = NULL WHERE id = a1.id» должно влиять на 0 строк, так как вы уже обновили все ab, где ab.a_id = a1.id.

Поэтому только одно возможное объяснение, вам не удалось обновить ab. Компьютеры, как правило, не лгут. Иногда мы просто теряем свою логику.

Мое решение: чтобы убедиться, что все в порядке, перед выдачей удаления, найдите все строки ab, указывающие на a1.id, если, как и следовало ожидать, вы найдете хотя бы один, попытайтесь понять, почему эта строка все еще там.

Возможно, session.db.add (ab) может быть заменен некоторым типом, таким как session.db.update (ab), а add() добавляет новые строки в вашу таблицу ab, или если обновление отсутствует или вы не можете его использовать, тогда сначала удалите ab, где ab.a_id = a1.id, после того, как вы правильно (я думаю, что у вас есть)

1

Всякий раз, когда возникают проблемы с синхронизацией между содержимым отношений и фактическим набором строк в базе данных, я рекомендую использовать session.expire(). Он заставляет значения перезагружаться из БД при следующем доступе к данным.

О истекают & обновление:

http://docs.sqlalchemy.org/en/latest/orm/session.html#refreshing-expiring

О несвежих данных в коллекциях:

http://docs.sqlalchemy.org/en/rel_0_8/orm/session.html#deleting-from-collections

Эти функции спасли мою жизнь. Теперь все работает так, как ожидалось. Более того, я могу делать массовые обновления через API sqlalchemy.sql.expression, который, как правило, намного быстрее, не жертвуя целостностью данных на уровне ORM.

+0

ahhhm ... Понятно. Я еще никогда не использовал sqlalchemy, но хорошо знать ... +1! – Daren

Смежные вопросы