2012-01-12 7 views
11

Questa è la prima volta che uso ORM, quindi non sono sicuro che sia il modo migliore per gestirlo. Ho una relazione uno-a-molti, dove ogni genitore può avere molti bambini:sqlalchemy aggiungi figlio nella relazione uno a molti

class Parent(Base): 
    __tablename__ = 'Parent' 

    name = Column(String(50)) 
    gid = Column(String(16), primary_key = True) 
    lastUpdate = Column(DateTime) 

    def __init__(self,name, gid): 
     self.name = name 
     self.gid = gid 
     self.lastUpdate = datetime.datetime.now() 


class Child(Base): 
    __tablename__ = 'Child' 

    id = Column(Integer, primary_key = True) 
    loc = Column(String(50)) 
    status = Column(String(50)) 

    parent_gid = Column(String(16), ForeignKey('Parent.gid')) 

    parent = relationship("Parent", backref=backref('children')) 

Ora, gli aggiornamenti sono venuta in attraverso la rete. Quando arriva un aggiornamento, voglio AGGIORNARE la riga Parent appropriata (aggiornando la colonna lastUpdate) e INSERIRE le nuove righe figli nel database. Non so come farlo con ORM. Qui è il mio tentativo fallito:

engine = create_engine('sqlite+pysqlite:///file.db', 
         module=dbapi2) 
Base.metadata.create_all(engine) 
session = sessionmaker(bind=engine)() 

def addChildren(parent): 
    p = session.query(Parent).filter(Parent.gid == p1.gid).all() 
    if len(p) == 0: 
     session.add(p1) 
     session.commit() 
    else: 
     updateChildren = parent.children[:] 
     parent.chlidren = [] 
     for c in updateChildren: 
      c.parent_gid = parent.gid 

     session.add_all(updateChildren) 
     session.commit() 

if __name__ == '__main__': 

    #first update from the 'network' 
    p1 = Parent(name='team1', gid='t1') 
    p1.children = [Child(loc='x', status='a'), Child(loc='y', status='b')] 
    addChildren(p1) 

    import time 
    time.sleep(1) 

    #here comes another network update 
    p1 = Parent(name='team1', gid='t1') 
    p1.children = [Child(loc='z', status='a'), Child(loc='k', status='b')] 
    #this fails 
    addChildren(p1) 

Inizialmente ho provato a fare una fusione, ma che ha causato i vecchi bambini di essere dissociati con il genitore (gli ID stranieri sono stati fissati a null). Qual è il modo migliore per avvicinarsi a questo con ORM? Grazie

EDIT

Credo che in realtà non ha senso per creare oggetti del tutto nuovi quando gli aggiornamenti sono disponibili in rete. Dovrei semplicemente interrogare la sessione per il genitore appropriato, quindi creare nuovi figli se necessario e unire? Per esempio.

def addChildren(pname, pid, cloc, cstat): 
    p = session.query(Parent).filter(Parent.gid == pid).all() 
    if len(p) == 0: 
     p = Parent(pname, pid) 
     p.children = [Child(loc=cloc, status=cstat)] 
     session.add(p) 
     session.commit() 
    else: 
     p = p[0] 
     p.children.append(Child(loc=cloc, status=cstat)) 
     session.merge(p) 
     session.commit() 

risposta

24

Hai ragione - non dovresti creare lo stesso genitore due volte. In termini di aggiunta di bambini, ... beh, è ​​davvero necessario solo aggiungerli e non ti interessa di quelli esistenti ... Quindi il tuo codice modificato dovrebbe fare il lavoro bene. Si può rendere più breve e più leggibile però:

def addChildren(pname, pid, cloc, cstat): 
    p = session.query(Parent).get(pid) # will give you either Parent or None 
    if not(p): 
     p = Parent(pname, pid) 
     session.add(p) 
    p.children.append(Child(loc=cloc, status=cstat)) 
    session.commit() 

Lo svantaggio di questo modo è che per il genitore esistente l'intera collezione di bambini verrà caricato in memoria prima viene aggiunto un nuovo bambino e poi salvato nel database. Se questo è il caso (molti e crescente numero di bambini per ciascun genitore), poi il lazy='noload' potrebbe essere utile:

parent = relationship("Parent", backref=backref('children', lazy='noload')) 

Questo potrebbe migliorare notevolmente la velocità di inserti, ma in questo caso l'accesso al p.children sarà mai carica gli oggetti esistenti dal database. In tali scenari è sufficiente definire un'altra relazione. In queste situazioni preferisco usare Building Query-Enabled Properties, quindi si finisce con una proprietà solo per l'aggiunta di oggetti e l'altra solo per la ricerca di risultati persistenti, che vengono spesso utilizzati da diverse parti del sistema.

+1

Per aggiungere un elenco di bambini è possibile utilizzare "p.children.extend ([list_of_children])", no? – aganders3

+1

sì, certo. Infatti, se siamo sicuri che il 'Parent' esiste per il dato' pid', e l'attività in corso è solo per memorizzare 'Child (ren)', ciò che si può fare non è caricare la relazione, ma impostare direttamente il Valore FK per 'parent_gid':' session.add (Child (loc = cloc, status = cstat, parent_gid = pid)) ', nel qual caso non è nemmeno necessario ingannare la relazione con' lazy = "noload" ', perché il codice non utilizzerà la relazione – van

+0

Puoi anche mostrare come aggiornare il bambino nello stesso scenario? –

Problemi correlati