问题
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