Query for list of attribute instead of tuples in SQLAlchemy

Question:

I’m querying for the ids of a model, and get a list of (int,) tuples back instead of a list of ids. Is there a way to query for the attribute directly?

result = session.query(MyModel.id).all()

I realize it’s possible to do

results = [r for (r,) in results]

Is it possible for the query to return that form directly, instead of having to process it myself?

Asked By: PapeK24

||

Answers:

When passing in ORM-instrumented descriptors such as a column, each result is a named tuple, even for just one column. You could use the column name in a list comprehension to ‘flatten’ the list (you can drop the .all() call, iteration retrieves the objects too):

result = [r.id for r in session.query(MyModel.id)]

or use the fact that it’s a tuple when looping a for loop and unpack it to a single-element tuple of targets:

result = session.query(MyModel.id)
for id, in result:
    # do something with the id

The latter could also be used in a list comprehension:

[id for id, in session.query(MyModel.id)]

You don’t really have any options to force the row results to be just the single id value.

Answered By: Martijn Pieters

It’s strange that SQLalchemy is not providing a proper solution. In sqlalchemy if select a member variable such as a column then each result is a named tuple as @Martijn said. I came to a solution for this using zip function of python

ZIP Method

Zip official docuementation

zip(seq1 [, seq2 […]]) -> [(seq1[0], seq2[0] …), (…)]
Return a list of tuples, where each tuple contains the i-th element
from each of the argument sequences. The returned list is truncated
in length to the length of the shortest argument sequence.

Coming to your example

result = session.query(MyModel.id).all()
result = zip(*result)[0]

Output:

[id1, id2, id3...]

How it will work it will flatten list of tuples given as argument if you pass the list like

[(key11, key21), (key12,key22)]

Zip will convert this list of tuples into

[(key11, key12), (key21, key22)]

In your case you want every Initial value of tupe that is of the MyModel so you can take the 0th tuple from the list.

Chain Method

from itertools import chain
result = session.query(MyModel.id).all()  # result [(id1,), (id2,), (id3,)]
result = list(chain(*result))

Output

[id1, id2, id3]

For loop

result = session.query(MyModel.id).all()  # result [(id1,), (id2,), (id3,)]
result = [id for id, in result]

Output

[id1, id2, id3]
Answered By: Anand Tripathi
q_stringlist = [q[0] for q in session.query((distinct(Table.column))).all()]                                                        

for Flask SQLAlchemy

q_stringlist = [q[0] for q in db.session.query((distinct(Table.column))).all()]   
Answered By: Patrick

With Result.scalars() you can create a ScalarResult to receive single objects instead of Rows. With the Core-style querying syntax your example could look like this:

>>> from sqlalchemy import select
>>> from sqlalchemy.orm import sessionmaker

>>> session = sessionmaker(...)
>>> result = session.execute(select(MyModel.id))
>>> result.scalars().all()
[1, 2, 3, ...]

Careful: If you select multiple columns .scalars().all() returns the first column only. So select(MyModel.id, MyModel.something) has the same ScalarResult as select(MyModel.id). However, to select all columns using select(MyModel), the ScalarResult is helpful again, as it returns a list of MyModel-objects.

Answered By: Kim
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.