2014-10-30 6 views
8

Ho quattro classi in questo modo: Group, Parent, Child, Toy.SQLAlchemy proxy dell'associazione concatenamento per pronipoti?

  • Group ha un rapporto parents indicando Parent
  • Parent ha un rapporto children indicando Child
  • Child ha un rapporto toys indicando Toy

Parent ha un toysassociation_proxy che produce tutti lo Toy s che i bambini di Parent hanno.

Voglio essere in grado di ottenere tutti i giocattoli in un gruppo. Ho cercato di creare un association_proxy su Group che si collega a Parent 's toys, ma produce questo:

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

quando voglio questo:

[<Toy 1>, <Toy 2>] 

Se il Parent' s children non hanno qualsiasi Toy s, quindi il proxy dell'associazione toys è []. Tuttavia, il secondo proxy di associazione non è in grado di escludere gli elenchi vuoti. Inoltre, gli elenchi dovrebbero essere compressi. C'è comunque per farlo funzionare?

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

Questa navigazione da (grand-) non-genitore è solo per la visualizzazione o anche per la modifica delle raccolte sottostanti? – van

+0

@van idealmente entrambi. –

+0

OK, in tal caso, come pensi di aggiungere giocattoli al 'Gruppo'? se fai 'my_group.toys.append (my_new_toy)', a cui 'Parent' e' Child' sarà assegnato? – van

risposta

4

Dato che quelli sono per il recupero e solo vista (come lei ha ricordato nel commento, aggiungendo sarebbe ambiguo), avrei preferito fare un viewonly rapporto senza un 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, 
    ) 

Si noti che questa è una nuova funzionalità di SQLAlchemy ed è descritta nella sezione Composite “Secondary” Joins della documentazione.

Quindi è possibile utilizzare solo per query:

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

o si può anche usare per filtrare, in modo da trovare un gruppo che contiene un giocattolo con il nome di "Super Mario" si può fare:

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

Ma in realtà tutto questo si può fare con semplice query, o creare una proprietà di query-enabled. Vedere la sezione Customizing Column Properties della documentazione, in cui è possibile utilizzare qualsiasi proprietà semplice, column_property o hybrid attribute.

+0

Fantastico, non ho visto nulla su queste relazioni di unione composita! Ci proverò. Sì, ho avuto richieste diverse con diversi join, ma aggiunge linee extra di cui mi piacerebbe sbarazzarmi. Inoltre il filtraggio lo rende ancora migliore! –