2014-10-30 10 views
8

Ich habe eine vier Klassen wie folgt: Group, Parent, Child, Toy.SQLAlchemy Chaining Association Proxy für Urenkel?

  • Group hat eine parents Beziehung zeigt auf Parent
  • Parent hat eine children Beziehung zu zeigen Child
  • Child hat eine toys Beziehung Toy

Parent zu zeigen hat ein toysassociation_proxy, dass alle produziert die Toy s dass die Parent 's Kinder haben.

Ich möchte in der Lage sein, alle Toys in einer Gruppe zu bekommen. Ich habe versucht, eine association_proxy auf Group, die Parent 's toys, aber es ergibt dies:

[[<Toy 1>, <Toy 2>], [], [], []] 

, wenn ich will dies:

[<Toy 1>, <Toy 2>] 

Wenn die Parent' Links erstellen s children nicht haben Alle Toy s, dann die toys Association Proxy ist []. Der zweite Zuordnungs-Proxy kann jedoch die leeren Listen nicht ausschließen. Außerdem sollten die Listen minimiert sein. Gibt es das überhaupt, um das zu schaffen?

class Group(db.Model): 
    id = db.Column(db.Integer, primary_key=True) 
    created_at = db.Column(db.DateTime, default=utils.get_now_datetime) 
    name = db.Column(db.String(80, convert_unicode=True)) 
    # relationships 
    parents = db.relationship('Parent', backref='group') 
    toys = association_proxy('parents', 'toys') 

class Parent(db.Model): 
    id = db.Column(db.Integer, primary_key=True) 
    group_id = db.Column(db.Integer, db.ForeignKey('group.id', ondelete='CASCADE')) 
    created_at = db.Column(db.DateTime, default=utils.get_now_datetime) 
    first_name = db.Column(db.String(80, convert_unicode=True)) 
    last_name = db.Column(db.String(80, convert_unicode=True)) 
    children = db.relationship('Child', backref='parent', cascade='all, delete') 
    toys = association_proxy('children', 'toys') 

class Child(db.Model): 
    id = db.Column(db.Integer, primary_key=True) 
    parent_id = db.Column(db.Integer, db.ForeignKey('parent.id', ondelete='CASCADE')) 
    created_at = db.Column(db.DateTime, default=utils.get_now_datetime) 

class Toy(db.Model): 
    id = db.Column(db.Integer, primary_key=True) 
    child_id = db.Column(db.Integer, db.ForeignKey('child.id', ondelete='CASCADE')) 
    created_at = db.Column(db.DateTime, default=utils.get_now_datetime) 
    child = db.relationship('Child', backref=db.backref("toys",cascade="all, delete-orphan", order_by="desc(Toy.id)")) 
+0

Ist diese Navigation von (Groß-) Großeltern nur zum Anzeigen oder auch zum Ändern der zugrunde liegenden Sammlungen? – van

+0

@van ideal beide. –

+0

OK, in diesem Fall, wie genau stellen Sie sich vor, der Gruppe "Spielzeug" hinzuzufügen? Wenn du 'my_group.toys.append (my_new_toy)', zu welchem ​​'Elternteil 'und' Kind 'wird es zugewiesen? – van

Antwort

4

Da diejenigen für den Abruf und die Aussicht sind nur (wie Sie im Kommentar erwähnt, wäre mehrdeutig Zugabe), würde ich lieber tun, um eine viewonly Beziehung ohne ein association_proxy:

class Group(db.Model): 
    # ... 
    toys = relationship('Toy', 
     secondary="join(Group, Parent, Group.id == Parent.group_id).join(Child, Parent.id == Child.parent_id)", 
     primaryjoin="and_(Group.id == Parent.group_id, Parent.id == Child.parent_id)", 
     secondaryjoin="Child.id == Toy.child_id", 
     viewonly=True, 
    ) 

Beachten Sie, dass dies eine neue Funktion von SQLAlchemy ist und im Abschnitt Composite “Secondary” Joins der Dokumentation beschrieben wird.

Dann können Sie es nur für die Suche nach:

group_id = 123 
group = session.query(Group).get(group_id) 
print(group.toys) 

Oder man kann es sogar zu filtern verwenden, so eine Gruppe zu finden, die mit dem Namen ein Spielzeug enthält „Super Mario“ Sie tun können:

group = session.query(Group).filter(Group.toys.any(Toy.name == "Super Mario")) 

Aber in Wirklichkeit das alles, was Sie mit einer einfachen Abfrage tun können, oder eine Abfrage-Eigenschaft enabled erstellen. Siehe Abschnitt Customizing Column Properties im Abschnitt der Dokumentation, in dem Sie eine der einfachen Eigenschaften column_property oder hybrid attribute verwenden können.

+0

Cool, habe noch nichts über diese zusammengesetzten Join-Beziehungen gesehen! Ich werde das versuchen. Ja, ich habe verschiedene Anfragen mit mehreren Joins, aber es fügt zusätzliche Zeilen hinzu, die ich gerne loswerden möchte. Plus-Filterung macht dies noch besser! –