2014-05-06 7 views
6

sagen, dass ich ein paar Objekte haben, die eine Beziehung einer Eins-zu-viele haben, so etwas wieFilter SQLAlchemy Abfrageergebnis Objekts one-to-many-Attribut

class Parent(): 
    //id, other cols, etc 
    children = relationship("Child", backref="parent") 

class Child(): 
    parent_id = Column(Integer, ForeignKey("parent.id") 
    child_type = Column(Enum("a","b")) 

Nun, ich möchte übergeordnete Objekte abfragen, haben aber ihre Kinder durch CHILD_TYPE, also so etwas wie

session.query(Parent).join(Parent.children).filter(Child.child_type == "a") 

gefiltert Dies gibt nur die Eltern mit allen Kindern, im Grunde die Filter zu vernachlässigen. Ist dieses Ergebnis überhaupt möglich oder muss ich auch Child abfragen?

Antwort

0

Sie versuchen, zwei Antworten in einer Abfrage zu erhalten. Entweder Sie können nach allen Eltern fragen, die ein Typ-Kind-Kind haben, oder Sie können nach allen Typ-A-Kindern fragen. Im ersten Fall müssen Sie die Kinder erneut filtern, wenn Sie die entsprechenden Kinder wollen, im zweiten Fall können Sie einfach die entsprechenden Eltern bekommen. Aber welcher Weg ist der richtige, hängt von dem weiteren Problem ab, das du zu lösen versuchst.

6

Tatsächlich fügt Ihre Abfrage einen Join und einen Filter hinzu, gibt aber nur Parent Instanzen zurück. In der Tat, nur diejenigen Parent Instanzen, die mindestens eine Child des Typs a haben.
Wenn Sie dann für jeden dieser Eltern auf .children zugreifen, wird eine neue SQL-Anweisung ausgegeben und alle Kinder dieser Eltern werden geladen. Sie können den Filter wieder im Speicher anwenden oder Ihre eigene Abfrage erstellen und nicht verlassen sich auf die Beziehung Navigation (kommentiert out) wie folgt:

# select *only* those parents who have at least one child of type "a" 
parents = session.query(Parent).join(Parent.children).filter(Child.child_type == "a") 
for p in parents: 
    # 1. in-memory filter: now select only type "a" children for each parent 
    children_a = [c for c in p.children if c.child_type == 'a'] 
    # 2. custom query: now select only type "a" children for each parent 
    # children_a = session.query(Child).with_parent(p).filter(Child.child_type == "a") 

    print("AAA", p) 
    for c in children_a: 
     print("AAA ..", c) 

Ein Weg, um es in einer Abfrage zu tun, ist unten dargestellt, aber Vorsicht wie Sie effektiv sagen, dass Sie geladen alle Kinder für die Eltern. Sie können diesen Ansatz für Szenarios verwenden, in denen Sie die Abfrage ausführen und dann die Sitzung verwerfen/recyceln:

# select all parents, and eager-load children of type "a" 
parents = (session.query(Parent) 
     .join(Parent.children).filter(Child.child_type == "a") 
     # make SA think we loaded all *parent.children* collection 
     .options(contains_eager('children')) 
     ) 

for p in parents: 
    children_a = p.children # now *children* are *incorrectly* filtered 
    print("BBB", p) 
    for c in children_a: 
     print("BBB ..", c)