4 votes

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

J'ai plusieurs fonctions où j'ai besoin de faire une jointure de type one-to-many, en utilisant count(), group_by et order_by. J'utilise la fonction sqlalchemy.select pour produire une requête qui me renverra un ensemble d'identifiants, que je parcours ensuite pour effectuer une sélection ORM sur les enregistrements individuels. Ce que je me demande, c'est 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 la itération.

Voici un exemple de ce que je fais actuellement. Dans ce cas, les entités sont Location et Guide, avec une relation one-to-many. J'essaie de obtenir une liste des principales locations triées par le nombre de guides auxquels elles sont liées.

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())

Solution

J'ai trouvé la meilleure façon de faire cela. Il suffit de fournir une from_statement au lieu d'un filter_by ou quelque chose du genre. Comme ceci:

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

2voto

J'ai découvert cela à mes dépens mais SQLAlchemy prend en charge group_by. La documentation sur Query ne le mentionne pas, mais cela est bien pris en charge - je l'ai utilisé !

De plus, vous pouvez également utiliser order_by. J'allais créer une classe/requête spéciale comme vous l'avez fait, mais j'ai découvert qu'il existe une fonction group_by().

1voto

Charles Duffy Points 34134

Ce que vous essayez de faire se rapporte directement à une jointure SQLAlchemy entre une sous-requête [issue de votre appel select actuel] et une table. Vous voudrez déplacer le tri en dehors de la sous-requête et créer une colonne séparée et étiquetée avec count(desc); ordonner le select externe par cette colonne.

À part cela, je ne vois pas grand-chose qui ne soit pas évident.

0voto

naturalethic Points 337

J'ai trouvé la meilleure façon de faire cela. Il suffit de fournir une from_statement au lieu d'un filter_by ou quelque chose de similaire. Comme ceci :

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