2017-06-20 11 views
3

Ich habe eine viele zu viele Beziehung Setup in einer Flask-App in SQLAlchemy mit einem Association-Objekt. Ich habe dann Verbindungs-Proxies zwischen den Klassen eingerichtet, um direkteren Zugriff zu ermöglichen, anstatt über das Assoziationsobjekt zu gehen.SQLAlchemy order_by viele zu viele Beziehung durch Assoziation Proxy

Hier ist ein verkürztes Beispiel des Aufbaus:

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 

Was kann ich von mir nicht für das Leben, herauszufinden, wie die Mitglieder von group.members werden sortiert nach ihrer last_name zurück zu erhalten?

+0

Sie don die Argumente 'uselist = False' brauchen nicht für die Beziehungen, wie sie auf der referenzierenden Seite definiert sind, oder anders ausgedrückt, eine' GroupMembership'-Entität kann nur auf eine 'Person' und eine 'Gruppe' verweisen. Sie würden es in eins-zu-eins-Beziehungen verwenden. –

+0

Nur um sicher zu sein, möchten Sie GroupMembership Objekte im Allgemeinen manuell behandeln? Mit anderen Worten, ist es üblich, dass Sie nur die GroupMemberships auswählen/abfragen? –

Antwort

5

Um group.members zu sortieren, müssen die Personen zum Sortieren vorhanden sein, während die GroupMembership-Assoziationsobjekte geladen werden. Dies kann mit einem Join erreicht werden.

In der aktuellen Konfiguration group.members erste lädt die GroupMembership Objekte, Füllen group.group_memberships Beziehung, und dann feuert ein SELECT für jede Person als der Verein Proxy greift die GroupMembership.person Beziehung Attribute zugreifen.

Stattdessen wollen Sie sowohl die GroupMemberships und Personen in derselben Abfrage laden, sortiert nach 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 

Sie müssen die order_by='Person.last_name' auf die skalare Beziehung GroupMembership.personGroup.group_memberships anstelle des backref Attribut definieren, die geeignet sind, scheint logisch zu sein. Auf der anderen Seite gibt order_by "die Reihenfolge an, die beim Laden dieser Elemente angewendet werden sollte", so dass es sinnvoll ist, wenn Sie joined loading verwenden. Da Sie einer Viele-zu-Eins-Referenz beitreten und der Fremdschlüssel nicht nullbar ist, können Sie einen Inner-Join verwenden.

Mit der Definition in Ort:

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'] 

Ein Nachteil dieser Lösung ist, dass die person Beziehung immer mit Spannung geladen wird, und die Reihenfolge angewandt, BY, wenn für GroupMemberships Abfrage:

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]: 
    ... 
Verwandte Themen