2012-12-25 19 views
8

Ho una classe:SQLAlchemy - Aggiornamento ForeignKey quando si imposta il rapporto

class ExampleClass(Base): 
    __tablename__ = 'chart' 
    id = Column(Integer, primary_key=True) 
    element_id = Column(Integer, ForeignKey('anotherTable.id')) 
    element = relationship(AnotherClass) 
    element2_id = Column(Integer, ForeignKey('anotherTable2.id')) 
    element2 = relationship(AnotherClass2) 

voglio fare una ricerca in base alla element_id e element2_id:

class ExampleClass(Base): 
    ... 
    def get_with_element2(self, element2): 
     return session.query(ExampleClass).\ 
         filter_by(element_id = self.element_id, 
           element2_id = element2.id).first() 

Il problema che ho trovato è che se Istanziare un nuovo oggetto ExampleClass e assegnarlo a element, il campo element_id non viene impostato:

a = ExampleClass(element=element_obj) 
a.element_id => None 

Come posso risolvere questo? Qual è il modo migliore per affrontare questo tipo di situazione?

risposta

9

Prima di tutto, tutti gli esempi seguenti presuppongono che l'istanza ExampleClass sia almeno nello stato pending se non lo stato "persistente" (ovvero, session.add(a)). In altre parole, se non si sta ancora interagendo con un e non si è aggiunto l'oggetto ExampleClass a uno, non si otterrà alcuno dei comportamenti a livello di database di relationship(), di cui il mantenimento dei valori di colonna chiave esterna è il principale caratteristica. Siete ovviamente liberi di fare questo compito direttamente:

a = ExampleClass(element_id=element_obj.id) 

ma questo non è, ovviamente, facendo uso dell'automazione fornita dal relationship() costrutto.

L'assegnazione di attributi chiave esterna da relationship() si verifica nel corso di una flush, che è un processo che si verifica solo quando è necessaria l'interazione con il database, ad esempio prima si emettono un'istruzione SQL utilizzando session.query() o prima di completare la transazione utilizzando session.commit() .

In generale, la filosofia di relationship() consiste nel fatto che si tratterebbe solo degli attributi "elemento" e "elemento2" qui e che gli attributi della chiave esterna vengano gestiti dietro le quinte. È possibile scrivere la query in questo modo:

session.query(ExampleClass).\ 
    filter_by(element=self.element).\ 
    filter_by(element2=element2) 

L'ORM avrà un confronto, come SomeClass.somerelationship=someobject e convertire che nell'espressione di chiave esterna SomeClass.some_fk=some_id, ma la differenza è, la valutazione del valore finale di "some_id" è differita fino alla destra prima dell'esecuzione della query. Prima che la query venga eseguita, l'oggetto dice a "autoflush" Session, che avrà l'effetto della riga ExampleClass inserita insieme all'identificativo chiave primaria di element_obj assegnato all'attributo element_id sull'oggetto ExampleClass.

si potrebbe ottenere un effetto simile, pur utilizzando l'FK attributi come questo, questo è per lo più solo per capire come funziona però:

session.query(ExampleClass).\ 
    filter_by(element_id=bindparam(callable_=lambda: self.element_id)).\ 
    filter_by(element2_id=element2.id) 

o anche più esplicito, fare la prima ondata:

session.flush() 
session.query(ExampleClass).\ 
    filter_by(element_id=self.element_id).\ 
    filter_by(element2_id=element2.id) 

Quindi, nella misura in cui si desidera fare riferimento a attributi di chiave esterna come element_id in modo esplicito, è necessario fare anche le cose che relationship() fa esplicitamente per voi.Se gestisci rigorosamente le istanze dell'oggetto e l'attributo-relationship() -bound e lascia le tipiche impostazioni predefinite come autoflush abilitate, generalmente eseguirà la "cosa giusta" e assicurati che gli attributi siano pronti quando necessario.

+0

Grazie mille per la spiegazione. Non sapevo se avrei potuto filtrare dalla relazione stessa e non volevo rovinare le cose. Quindi sicuramente questo è il modo in cui lo farò. –

Problemi correlati