4 votes

SQLAlchemy avec count, group_by et order_by en utilisant l'ORM

J'ai plusieurs fonctions pour lesquelles j'ai besoin de faire une jointure un à plusieurs, en utilisant count(), group_by et order_by. J'utilise la fonction sqlalchemy.select pour produire une requête qui me renvoie un ensemble d'identifiants, sur lesquels j'itère ensuite pour effectuer une sélection ORM sur les enregistrements individuels. Je me demande s'il existe un moyen de faire ce dont j'ai besoin en utilisant l'ORM dans une seule requête afin d'éviter d'avoir à faire l'itération.

Voici un exemple de ce que je suis en train de faire. Dans ce cas, les entités sont Location et Guide, mappées en one-to-many. J'essaie d'obtenir une liste des principaux lieux triés en fonction du nombre de guides auxquels ils sont liés.

def popular_world_cities(self):
    query = select([locations.c.id, func.count(Guide.location_id).label('count')],
                   from_obj=[locations, guides],
                   whereclause="guides.location_id = locations.id AND (locations.type = 'city' OR locations.type = 'custom')",
                   group_by=[Location.id],
                   order_by='count desc',
                   limit=10)
    return map(lambda x: meta.Session.query(Location).filter_by(id=x[0]).first(), meta.engine.execute(query).fetchall())

Solución

J'ai trouvé le meilleur moyen de le faire. Il suffit de fournir un from_statement au lieu d'un filter_by ou autre. Comme ça :

meta.Session.query(Location).from_statement(query).all()

2voto

J'ai découvert cela à la dure, mais SQLAlchemy prend en charge group_by . La documentation sous Query ne le dit pas mais elle le supporte - je l'ai utilisé !

Et, vous pouvez aussi utiliser order_by . J'allais créer une classe/quête spéciale comme vous l'avez fait, mais j'ai découvert qu'il existait une méthode d'évaluation de la qualité de l'eau. group_by() fonction.

1voto

Charles Duffy Points 34134

Ce que vous essayez de faire correspond directement à une jointure SQLAlchemy entre une sous-requête [faite à partir de votre appel select actuel] et une table. Vous devez déplacer le classement hors de la sous-sélection et créer une colonne séparée, étiquetée, avec count(desc) ; classez la sélection externe par cette colonne.

A part ça, je ne vois pas grand chose de non évident.

0voto

naturalethic Points 337

J'ai trouvé le meilleur moyen de le faire. Il suffit de fournir un from_statement au lieu d'un filter_by ou autre. Comme ça :

meta.Session.query(Location).from_statement(query).all()

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X