2017-06-20 1 views
3

J'ai une configuration de plusieurs à plusieurs relations dans une application Flask dans SQLAlchemy en utilisant un objet Association. J'ai alors des proxies d'assocation configurées entre les classes, pour donner un accès plus direct plutôt que d'aller via l'objet association.SQLAlchemy order_by plusieurs à plusieurs relation par l'intermédiaire de proxy d'association

Voici un exemple abrégé de la configuration:

class Person(Model): 
    __tablename__ = 'persons' 
    id = Column(Integer, primary_key=True) 
    last_name = Column(Text, nullable=False) 
    groups = association_proxy('group_memberships', 'group') 
    # Other stuff 

class Group(Model): 
    __tablename__ = 'groups' 
    id = Column(Integer, primary_key=True) 
    name = Column(Text, nullable=False) 
    members = association_proxy('group_memberships', 'person') 
    # Other stuff 

class GroupMembership(Model): 
    __tablename__ = 'group_memberships' 
    id = Column(Integer, primary_key=True) 
    person_id = Column(Integer, ForeignKey('persons.id'), nullable=False) 
    group_id = Column(Integer, ForeignKey('groups.id'), nullable=False) 
    person = relationship('Person', uselist=False, backref=backref('group_memberships', cascade='all, delete-orphan')) 
    group = relationship('Group', uselist=False, backref=backref('group_memberships', cascade='all, delete-orphan'))  
    # Other stuff 

Ce que je ne peux pas pour la vie de moi à comprendre comment obtenir les membres retournés par group.members à trier par leur last_name?

+0

Vous n Vous n'avez pas besoin des arguments 'uselist = False' sur les relations telles qu'elles sont définies du côté de la référence, ou en d'autres termes, une entité' GroupMembership' ne peut référencer qu'un seul 'Person' et' Group'. Vous l'utiliseriez dans des relations individuelles. –

+0

Pour être sûr, souhaitez-vous gérer manuellement les objets GroupMembership en général? En d'autres termes, est-il courant de sélectionner/interroger uniquement les GroupMemberships? –

Répondre

5

Pour trier group.members vous devez avoir les Personnes disponibles pour le tri lors du chargement des objets de l'association GroupMembership. Cela peut être réalisé avec une jointure.

Dans votre configuration actuelle l'accès group.members charge d'abord la objets de GroupMembership, remplissage group.group_memberships relation, et se déclenche un SELECT pour chaque personne que le proxy d'association accède aux attributs relation GroupMembership.person.

Au lieu de cela que vous voulez charger les deux GroupMemberships et des personnes dans la même requête, triés par Person.last_name:

class GroupMembership(Model): 
    __tablename__ = 'group_memberships' 
    id = Column(Integer, primary_key=True) 
    person_id = Column(Integer, ForeignKey('persons.id'), nullable=False) 
    group_id = Column(Integer, ForeignKey('groups.id'), nullable=False) 
    person = relationship('Person', 
          backref=backref('group_memberships', 
              cascade='all, delete-orphan'), 
          lazy='joined', innerjoin=True, 
          order_by='Person.last_name') 
    group = relationship('Group', backref=backref('group_memberships', 
                cascade='all, delete-orphan')) 
    # Other stuff 

Vous devez définir la order_by='Person.last_name' sur la relation scalaire attribut GroupMembership.person au lieu du backref Group.group_memberships, qui pourrait semble être la chose logique à faire. D'autre part, order_by "indique la commande qui doit être appliquée lors du chargement de ces éléments", il est donc logique d'utiliser joined loading. Puisque vous allez rejoindre une référence many-to-one et que la clé étrangère n'est pas Nullable, vous pouvez utiliser une jointure interne.

Avec la définition donnée en place:

In [5]: g = Group(name='The Group') 

In [6]: session.add_all([GroupMembership(person=Person(last_name=str(i)), group=g) 
    ...:     for i in range(30, 20, -1)]) 

In [7]: session.commit() 

In [8]: g.members 
2017-06-29 09:17:37,652 INFO sqlalchemy.engine.base.Engine BEGIN (implicit) 
2017-06-29 09:17:37,653 INFO sqlalchemy.engine.base.Engine SELECT groups.id AS groups_id, groups.name AS groups_name 
FROM groups 
WHERE groups.id = ? 
2017-06-29 09:17:37,653 INFO sqlalchemy.engine.base.Engine (1,) 
2017-06-29 09:17:37,655 INFO sqlalchemy.engine.base.Engine SELECT group_memberships.id AS group_memberships_id, group_memberships.person_id AS group_memberships_person_id, group_memberships.group_id AS group_memberships_group_id, persons_1.id AS persons_1_id, persons_1.last_name AS persons_1_last_name 
FROM group_memberships JOIN persons AS persons_1 ON persons_1.id = group_memberships.person_id 
WHERE ? = group_memberships.group_id ORDER BY persons_1.last_name 
2017-06-29 09:17:37,655 INFO sqlalchemy.engine.base.Engine (1,) 
Out[8]: [<__main__.Person object at 0x7f8f014bdac8>, <__main__.Person object at 0x7f8f014bdba8>, <__main__.Person object at 0x7f8f014bdc88>, <__main__.Person object at 0x7f8f01ddc390>, <__main__.Person object at 0x7f8f01ddc048>, <__main__.Person object at 0x7f8f014bdd30>, <__main__.Person object at 0x7f8f014bde10>, <__main__.Person object at 0x7f8f014bdef0>, <__main__.Person object at 0x7f8f014bdfd0>, <__main__.Person object at 0x7f8f0143b0f0>] 

In [9]: [p.last_name for p in _] 
Out[9]: ['21', '22', '23', '24', '25', '26', '27', '28', '29', '30'] 

Un inconvénient de cette solution est que la relation person est toujours chargé avec impatience et ORDER BY appliquée lors de l'interrogation pour GroupMemberships:

In [11]: session.query(GroupMembership).all() 
2017-06-29 12:33:28,578 INFO sqlalchemy.engine.base.Engine BEGIN (implicit) 
2017-06-29 12:33:28,578 INFO sqlalchemy.engine.base.Engine SELECT group_memberships.id AS group_memberships_id, group_memberships.person_id AS group_memberships_person_id, group_memberships.group_id AS group_memberships_group_id, persons_1.id AS persons_1_id, persons_1.last_name AS persons_1_last_name 
FROM group_memberships JOIN persons AS persons_1 ON persons_1.id = group_memberships.person_id ORDER BY persons_1.last_name 
2017-06-29 12:33:28,578 INFO sqlalchemy.engine.base.Engine() 
Out[11]: 
    ...