web-dev-qa-db-fra.com

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

J'ai un modèle de chaussure comme celui-ci:

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'identifiants comme ids = [2,1,3] et lorsque je pose une requête sur le modèle Shoe de sorte que les résultats comportent des identifiants dans la liste "ids", je souhaite revenir: [{id: 2, asin: " 111 "}, {id: 1, asin:" 113 "}, {id: 3, asin:" 42 "}], mais le problème est que l'utilisation de l'instruction de requête suivante ne conserve pas l'ordre d'origine, les résultats retour au hasard. Comment garder l'ordre de la liste que j'ai filtrée?

Un incorrect: Shoe.query.filter(Shoe.id.in_(my_list_of_ids)).all()

11
user1835351

Si vous avez une petite liste d'identifiants raisonnable, vous pouvez simplement exécuter des requêtes SQL sur chaque identifiant individuellement:

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

Pour un grand nombre d'identifiants, les requêtes SQL prendront beaucoup de temps. Vous ferez alors mieux avec une seule requête et en plaçant les valeurs dans le bon ordre dans une seconde étape (emprunté à comment sélectionner un objet dans 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 identifiants sont uniques (ce qui devrait être le cas dans votre cas). La première méthode lève une exception s'il existe plusieurs éléments ayant le même identifiant.

11
Rob

Par le passé, j'ai déjà résolu ce problème en utilisant un SQL CASE expression pour indiquer à la base de données dans quel ordre les lignes doivent être 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()
6
bjmc

J'ai également 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 voici à quoi ressemble ma requête:

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: Votre requête devrait donc 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()

À votre santé

0