Sqlalchemy 按列表中的字段过滤但保持原始顺序?

Sqlalchemy filter by field in list but keep original order?

我有这样的鞋款:

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

我有一个像 ids = [2,1,3] 这样的 id 列表,当我查询 Shoe 模型时结果在 'ids' 列表中有 id,我想返回: [{id:2, asin:"111"},{id:1, asin:"113"},{id:3, asin:"42"}] 但问题是使用下面的查询语句不会保留原始顺序,结果将随机返回。如何保持我筛选的列表的顺序?

不正确的一个:Shoe.query.filter(Shoe.id.in_(my_list_of_ids)).all()

你说 "original order" 是什么意思?数据库没有 "original order" 这样的东西。如果您需要一些订单,您必须添加类似:

.order_by(Shoe.id.desc())

如果您不指定顺序,您仍然有可能从数据库中获取有序数据。但是在这种情况下,数据库只是使用不需要任何不必要的数据操作的顺序。它只是看起来像一个有序的数据,但它不是。

如果您有一个合理的小型 ID 列表,您可以对每个 ID 单独执行 SQL 查询:

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

id数量多,SQL查询时间会比较长。然后,您最好使用单个查询并在第二步中以正确的顺序放置值(从 how to select an object from a list of objects by its attribute in 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]

这是假设 ID 是唯一的(在您的情况下它们应该是唯一的)。如果有多个具有相同 id 的元素,第一个方法将引发异常。

我在使用 MySQL 数据库时也遇到了同样的问题。这就是我所做的:

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

我的查询是这样的:

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

FIELD() 是 MySQL 中的原生函数。

编辑:因此您的查询应如下所示:

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

干杯

我过去解决这个问题的一种方法是使用 SQL CASE expression 告诉数据库我希望行返回的顺序。使用您的示例:

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