Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQL Alchemy ORM returning a single column, how to avoid common post processing

I'm using SQL Alchemy's ORM and I find when I return a single column I get the results like so:

[(result,), (result_2,)] # etc... 

With a set like this I find that I have to do this often:

results = [r[0] for r in results] # So that I just have a list of result values 

This isn't that "bad" because my result sets are usually small, but if they weren't this could add significant overhead. The biggest thing is I feel it clutters the source, and missing this step is a pretty common error I run into.

Is there any way to avoid this extra step?

A related aside: This behaviour of the orm seems inconvenient in this case, but another case where my result set was, [(id, value)] it ends up like this:

[(result_1_id, result_1_val), (result_2_id, result_2_val)] 

I then can just do:

results = dict(results) # so I have a map of id to value 

This one has the advantage of making sense as a useful step after returning the results.

Is this really a problem or am I just being a nitpick and the post processing after getting the result set makes sense for both cases? I'm sure we can think of some other common post processing operations to make the result set more usable in the application code. Is there high performance and convenient solutions across the board or is post processing unavoidable, and merely required for varying application usages?

When my application can actually take advantage of the objects that are returned by SQL Alchemy's ORM it seems extremely helpful, but in cases where I can't or don't, not so much. Is this just a common problem of ORMs in general? Am I better off not using the ORM layer in cases like this?

I suppose I should show an example of the actual orm queries I'm talking about:

session.query(OrmObj.column_name).all() 

or

session.query(OrmObj.id_column_name, OrmObj.value_column_name).all() 

Of course, in a real query there'd normally be some filters, etc.

like image 432
Derek Litz Avatar asked Feb 28 '12 16:02

Derek Litz


People also ask

What is lazy true in SQLAlchemy?

Typically when you query the database, the data get loaded at once; however, lazy parameter allows you to alternate the way they get loaded. lazy = 'select' (or True)

What does First () do in SQLAlchemy?

first() applies a limit of one within the generated SQL, so that only one primary entity row is generated on the server side (note this may consist of multiple result rows if join-loaded collections are present). Calling Query. first() results in an execution of the underlying query.

What does SQLAlchemy all () return?

As the documentation says, all() returns the result of the query as a list.


2 Answers

One way to decrease the clutter in the source is to iterate like this:

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

Although this solution is one character longer than using the [] operator, I think it's easier on the eyes.

For even less clutter, remove the parenthesis. This makes it harder when reading the code, to notice that you're actually handling tuples, though:

results = [r for r, in results] 
like image 118
Dag Høidahl Avatar answered Sep 25 '22 13:09

Dag Høidahl


Python's zip combined with the * inline expansion operator is a pretty handy solution to this:

>>> results = [('result',), ('result_2',), ('result_3',)] >>> zip(*results) [('result', 'result_2', 'result_3')] 

Then you only have to [0] index in once. For such a short list your comprehension is faster:

>>> timeit('result = zip(*[("result",), ("result_2",), ("result_3",)])', number=10000) 0.010490894317626953 >>> timeit('result = [ result[0] for result in [("result",), ("result_2",), ("result_3",)] ]', number=10000) 0.0028390884399414062 

However for longer lists zip should be faster:

>>> timeit('result = zip(*[(1,)]*100)', number=10000) 0.049577951431274414 >>> timeit('result = [ result[0] for result in [(1,)]*100 ]', number=10000) 0.11178708076477051 

So it's up to you to determine which is better for your situation.

like image 29
Beright Avatar answered Sep 23 '22 13:09

Beright