2012-08-07 12 views
7

Questo non è davvero un problema, voglio solo capire. Considerando il seguente codice:Comportamento di eliminazione con relazione

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() 

voglio record da AB tabella di essere rimosso quando i record correlati da B vengono rimossi. Ho provato 3 tipi di relazioni (il check-in tabella B):

  • 1: Non funziona (AssertionError: regola Dipendenza cercato di vuoto-out primaria colonna chiave 'ab.id_b' nell'istanza '') , mentre se si tenta di rimuoverlo direttamente nel database, i vincoli vengono utilizzati correttamente e vengono rimossi i record di AB.

  • 2: Opere, non capisco il motivo per cui questo è necessario perché i database generati sono identiche (è possibile controllare il diff in uscita)

  • 3: le opere, i vincoli DB fare il lavoro.

Lasciando (3) a parte, non ottengo perché è necessario (2), perché il ondelete='cascade' è già impostato, e generato DB è identico. La mia ipotesi sarebbe con (1), SQLAlchemy ha abbastanza informazioni per avere il comportamento corretto.

Mi manca qualcosa? Grazie.

+1

Grazie mille per questa domanda. Davvero utile – clime

risposta

10

cascade su relationship configura le operazioni di cascata Session, ad esempio Session.delete. È indipendente dalle eventuali direttive ON X CASCADE che si possono avere sui vincoli delle chiavi esterne nel database stesso.

Nel tuo caso, avere cascade='all' indica a SQLAlchemy di mettere in cascata Session.delete (tra le altre operazioni) dall'oggetto padre (AB) all'oggetto figlio. Senza di esso, la modalità di funzionamento predefinita è quella di inserire NULL nella colonna della chiave esterna e lasciare che sia l'oggetto di riferimento.

D'altra parte, passive_deletes=True indica a SQLAlchemy di fare affidamento sul database per pulire gli oggetti eliminati tramite le direttive ON DELETE CASCADE. Ciò impedisce a SQLAlchemy di emettere la query DELETE da sola, come farebbe nel caso relationship(cascade=...).

+0

Questo è più chiaro del documento, grazie :) – tonio

+4

questa è la sezione doc che hai letto? http://docs.sqlalchemy.org/en/rel_0_7/orm/session.html#cascades Ho revisionato quella sezione decine di volte per anni, ne ho ancora bisogno? Di meno ? cosa ti ha buttato fuori? – zzzeek

Problemi correlati