2013-07-10 13 views
11

Sto provando a creare una relazione che si estende su 3 tabelle ma non riesco a capire la sintassi.Relazioni SQLAlchemy su più tabelle

Ho 3 tabelle TableA, TableB e TableC e il rapporto che sto cercando di modello è:

TableA.my_relationship = relationship(
    'TableC', 
    primaryjoin='and_(TableA.fk == TableB.pk, TableB.fk == TableC.pk)', 
    viewonly=True 
) 

in modo che su un'istanza di TableA posso fare instance_of_a.my_relationship per vedere la scheda TableC che è indirettamente associata con instance_of_a

risposta

15
from sqlalchemy import * 
from sqlalchemy.orm import * 
from sqlalchemy.ext.declarative import declarative_base 

Base = declarative_base() 

class A(Base): 
    __tablename__ = 'a' 

    id = Column(Integer, primary_key=True) 
    b_id = Column(Integer, ForeignKey('b.id')) 

    # method one - put everything into primaryjoin. 
    # will work for simple lazy loads but for eager loads the ORM 
    # will fail to build up the FROM to correctly include B 
    cs = relationship("C", 
       # C.id is "foreign" because there can be many C.ids for one A.id 
       # B.id is "remote", it sort of means "this is where the stuff 
       # starts that's not directly part of the A side" 
       primaryjoin="and_(A.b_id == remote(B.id), foreign(C.id) == B.c_id)", 
       viewonly=True) 

    # method two - split out the middle table into "secondary". 
    # note 'b' is the table name in metadata. 
    # this method will work better, as the ORM can also handle 
    # eager loading with this one. 
    c_via_secondary = relationship("C", secondary="b", 
         primaryjoin="A.b_id == B.id", secondaryjoin="C.id == B.c_id", 
         viewonly=True) 
class B(Base): 
    __tablename__ = 'b' 

    id = Column(Integer, primary_key=True) 
    c_id = Column(Integer, ForeignKey('c.id')) 

class C(Base): 
    __tablename__ = 'c' 
    id = Column(Integer, primary_key=True) 

e = create_engine("sqlite://", echo=True) 
Base.metadata.create_all(e) 

sess = Session(e) 

sess.add(C(id=1)) 
sess.flush() 
sess.add(B(id=1, c_id=1)) 
sess.flush() 
sess.add(A(b_id=1)) 
sess.flush() 

a1 = sess.query(A).first() 
print(a1.cs) 

print(a1.c_via_secondary) 
+0

Un metodo di definizione delle relazioni preferito dall'altra per qualsiasi motivo? Il riferimento esplicito di 'secondary' nel metodo two sembra intuitivo rispetto al complesso' primaryjoin' str nel metodo uno ... ma il metodo uno sembra che potrebbe essere più potente. – Russ

+0

questo è un po 'strano, quindi per quanto secondario, non è così che "secondario" era inteso per essere usato originariamente, ad es. si aspetta che i FK siano tutti nel mezzo. Non sono sicuro che quella relazione "Secondaria" riuscirà effettivamente a persistere correttamente, ad es. potrebbe essere necessario essere visibile = Vero anche. L'ORM farà anche diverse scelte quando costruisce cose come carichi desiderosi e, a tale riguardo, la versione "secondaria" può prendere decisioni migliori poiché sa che c'è una tabella aggiuntiva nella clausola FROM. – zzzeek

+0

confermato, unito a carico caricamento funziona solo con il secondo, il primo non riesce a creare correttamente il file – zzzeek

Problemi correlati