Sqlalchemy filtre par champ dans la liste mais garder l'ordre original?

j'ai un modèle de Chaussure comme ceci:

class Shoe(db.Model):
id = db.Column(db.Integer, primary_key = True)
asin = db.Column(db.String(20), index = True)

j'ai une liste d'ids comme ids = [2,1,3] et quand j'interroge sur le modèle de chaussure tel que les résultats ont ids dans la liste' ids', je veux revenir: [{id:2, asin:"111"},{id:1, asin:"113"},{id:3, asin:"42"}] mais le problème est que l'utilisation de la requête suivante ne préserve pas l'ordre original, les résultats reviendront au hasard. Comment maintenir l'ordre de la liste que j'ai filtrée?

Incorrect: Shoe.query.filter(Shoe.id.in_(my_list_of_ids)).all()

10
demandé sur user1835351 2015-03-29 09:26:57

4 réponses

si vous avez une petite liste raisonnable d'ids, vous pouvez juste effectuer des requêtes SQL sur chaque id individuellement:

[Shoe.query.filter_by(id=id).one() for id in my_list_of_ids]

pour un grand nombre d'ids, les requêtes SQL prendront beaucoup de temps. Alors, vous êtes mieux avec une seule requête et mettre les valeurs dans l'ordre correct dans une deuxième étape (emprunté comment sélectionner un objet à partir d'une liste d'objets par son attribut en python):

shoes = Shoe.query.filter(Shoe.id.in_(my_list_of_ids)).all()
[next(s for s in shoes if s.id == id) for id in my_list_of_ids]

ceci suppose que les id sont uniques (ce qu'ils devraient être dans votre cas). La première méthode va soulever une exception s'il y a plusieurs éléments avec le même id.

9
répondu Rob 2017-05-23 12:26:37

une façon que j'ai résolu ce problème dans le passé est en utilisant un SQL CASE expression pour indiquer à la base de données dans quel ordre je voudrais que les lignes soient retournées. En utilisant votre exemple:

from sqlalchemy.sql.expression import case

ordering = case(
    {id: index for index, id in enumerate(my_list_of_ids)},
    value=Shoe.id
 )
Shoe.query.filter(Shoe.id.in_(my_list_of_ids)).order_by(ordering).all()
4
répondu bjmc 2017-08-24 20:55:43

j'ai aussi le même problème en utilisant une base de données MySQL. C'est ce que j'ai fait:

my_list = [13,14,5,6,7]
# convert my_list to str
my_list_str = ','.join(map(str, my_list))

Et c'est ainsi que ma requête est de la forme:

checkpoints = (
    db_session.query(Checkpoint)
    .filter(Checkpoint.id.in_(my_list))
    .order_by('FIELD(id, ' + my_list_str + ')')
    .all()
)

FIELD() est une fonction native de MySQL.

EDIT: Si votre requête devrait ressembler à ceci:

my_list_of_ids_str = ','.join(map(str, my_list_of_ids)) 
Shoe.query.filter(Shoe.id.in_(my_list_of_ids)).order_by('FIELD(id, ' + my_list_of_ids_str + ')').all()

Cheers

0
répondu Earth Ponce Maniebo 2016-06-19 08:55:08

Que voulez-vous dire par "ordre d'origine"? La base de données n'a pas de "commande originale". Si vous avez besoin d'un peu d'ordre, vous devez ajouter quelque chose comme:

.order_by(Shoe.id.desc())

si vous ne spécifiez pas la commande, il est toujours possible que vous obteniez des données commandées à partir de la base de données. Mais dans ce cas, la base de données utilise juste l'ordre qui n'a pas besoin de manipulation de données inutile. Cela ressemble à des données ordonnées, mais ce n'est pas le cas.

-1
répondu Alexander R. 2015-03-29 07:13:09