2012-08-07 12 views
7

Dies ist nicht wirklich ein Problem, ich will nur verstehen. Betrachtet man den folgenden Code:Löschverhalten mit Beziehung

from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy import * 
from sqlalchemy.orm import sessionmaker, relationship 
Base = declarative_base() 

class AB(Base): 
    __tablename__= 'ab' 
    id_a = Column(Integer, ForeignKey('a.id', ondelete='CASCADE'), primary_key=True) 
    id_b = Column(Integer, ForeignKey('b.id', ondelete='CASCADE'), primary_key=True) 
    rel = Column(Unicode) 

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

class B(Base): 
    __tablename__ = 'b' 
    id = Column(Integer, primary_key=True) 
    #1: doesn’t work try to set id_b to null 
    rel_a = relationship('AB') 
    # Works, but cascade='all' seems uneeded to me 
    rel_a = relationship('AB', cascade='all') 
    # Works 
    rel_a = relationship('AB', passive_deletes=True) 

engine = create_engine('sqlite://', echo=True) 

import logging 
logger = logging.getLogger('sqlalchemy.engine.base.Engine') 
logger.setLevel(logging.DEBUG) 
handler = logger.handlers[0] 
handler.setLevel(logging.DEBUG) 
handler.setFormatter(logging.Formatter('%(levelname)s %(message)s', '')) 

Base.metadata.create_all(engine) 

sess = sessionmaker(engine)() 

a1 = A() 
b1 = B() 
ab = AB() 

sess.add_all([a1,b1]) 
sess.flush() 

ab.id_a = a1.id 
ab.id_b = b1.id 
ab.rel = u'truite' 
sess.add(ab) 
sess.flush() 
sess.delete(b1) 
sess.flush() 

Ich möchte Datensätze aus AB Tabelle entfernt werden, wenn verknüpfte Datensätze aus B entfernt werden. Ich habe versucht, drei Arten von Beziehungen (Check-in B Tabelle):

  • 1: Funktioniert nicht (AssertionError: Abhängigkeit der Regel versucht, Ausblendung Primärschlüsselspalte 'ab.id_b' auf Instanz '') Wenn dagegen versucht wird, es direkt in der Datenbank zu entfernen, werden Einschränkungen korrekt verwendet, und Datensätze von AB werden entfernt.

  • 2: Works, ich verstehe nicht, warum dies erforderlich ist, weil erzeugten Datenbanken identisch sind (Sie die diff auf Ausgang überprüfen)

  • 3: Arbeiten, tun die DB Zwänge die Arbeit.

Weggehen (3) auseinander, erhalte ich nicht, warum (2) erforderlich ist, weil die ondelete='cascade' bereits gesetzt ist, und erzeugt DB identisch ist. Meine Vermutung wäre mit (1), SQLAlchemy hat genug Informationen, um das richtige Verhalten zu haben.

Fehle ich etwas? Vielen Dank.

+1

Vielen Dank für diese Frage. Wirklich hilfreich. – clime

Antwort

10

cascade on relationship konfiguriert die Kaskaden von Session Operationen, wie Session.delete. Es ist unabhängig von allen ON X CASCADE Direktiven, die Sie möglicherweise in Ihren Fremdschlüsseln in der Datenbank selbst haben.

In Ihrem Fall mit cascade='all' sagt SQLAlchemy die Session.delete (neben anderen Operationen) vom übergeordneten Objekt (AB) an das untergeordnete Objekt kaskadieren. Ohne es ist die Standardbetriebsart, NULL in die Fremdschlüsselspalte zu setzen und das referenzierte Objekt zu lassen.

Auf der anderen Seite weist passive_deletes=True SQLAlchemy an, sich auf die Datenbank zu verlassen, um gelöschte Objekte über die ON DELETE CASCADE-Anweisungen zu bereinigen. Dies verhindert, dass SQLAlchemy die DELETE-Abfrage selbst ausgibt, wie es im Fall relationship(cascade=...) der Fall wäre.

+0

Das ist klarer als das doc, danke :) – tonio

+4

ist das der doc-Abschnitt, den Sie gelesen haben? http://docs.sqlalchemy.org/en/rel_0_7/orm/session.html#cascades Ich habe diesen Bereich seit Jahren dutzende Male überarbeitet, braucht noch mehr? Weniger ? Was hat dich davon abgebracht? – zzzeek