Sqlalchemy filter by field in list but keep original order?

久未见 提交于 2019-12-04 17:35:56

问题


I have a Shoe model like this:

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

I have a list of ids like ids = [2,1,3] and when I query on the Shoe model such that the results have ids in the 'ids' list, I want back: [{id:2, asin:"111"},{id:1, asin:"113"},{id:3, asin:"42"}] but the problem is that using the following query statement doesn't preserve the original order, the results will come back random. How do I keep the order of the list I filtered by?

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


回答1:


If you have a reasonable small list of ids, you could just perform SQL queries on each id individually:

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

For a large number of ids, SQL queries will take a long time. Then you are better off with a single query and putting the values in the correct order in a second step (borrowed from 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]

This is assuming the id's are unique (which they should be in your case). The first method will raise an exception if there are multiple elements with the same id.




回答2:


One way I've solved this problem in the past is by using a SQL CASE expression to tell the database in what order I'd like the rows returned. Using your example:

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



回答3:


I also have the same problem using a MySQL database. This is what I did:

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

And this is how my query looks like:

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

FIELD() is a native function in MySQL.

EDIT: So your query should look like this:

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




回答4:


What do you mean when you say "original order"? Database doesn't have such thing as "original order". If you need some order, you must add something like:

.order_by(Shoe.id.desc())

If you don't specify the order, it's still possible that you get ordered data from database. But in this case, database just uses order that doesn't needs any unnecessary data manipulation. It's just looks like an ordered data, but it isn't.



来源:https://stackoverflow.com/questions/29326297/sqlalchemy-filter-by-field-in-list-but-keep-original-order

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!