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