SQL Alchemy ORM devuelve una sola columna, cómo evitar el procesamiento posterior común

Estoy usando el ORM de Alquimia de SQL y encuentro que cuando devuelvo una sola columna, obtengo los resultados así:

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

Con un set como este encuentro que tengo que hacer esto a menudo:

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

Esto no es tan “malo” porque mis conjuntos de resultados suelen ser pequeños, pero si no lo fueran, esto podría agregar una sobrecarga significativa. Lo más importante es que siento que desordena la fuente, y omitir este paso es un error bastante común con el que me encuentro.

¿Hay alguna forma de evitar este paso extra?

Un aspecto relacionado: este comportamiento del orm parece inconveniente en este caso, pero otro caso en el que mi conjunto de resultados fue, [(id, valor)] termina así:

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

Entonces puedo simplemente hacer:

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

Este tiene la ventaja de tener sentido como un paso útil después de devolver los resultados.

¿Es esto realmente un problema o estoy siendo un imbécil y el procesamiento posterior después de obtener el conjunto de resultados tiene sentido para ambos casos? Estoy seguro de que podemos pensar en otras operaciones comunes de procesamiento posterior para hacer que el conjunto de resultados sea más utilizable en el código de la aplicación. ¿Existe un alto rendimiento y soluciones convenientes en todos los ámbitos o el procesamiento posterior es inevitable, y se requiere simplemente para diferentes usos de aplicaciones?

Cuando mi aplicación realmente puede aprovechar los objetos que devuelve el ORM de SQL Alchemy, me parece extremadamente útil, pero en los casos en que no puedo o no, no tanto. ¿Es esto solo un problema común de ORMs en general? ¿Es mejor no usar la capa ORM en casos como este?

Supongo que debería mostrar un ejemplo de las consultas reales de orm de las que estoy hablando:

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

o

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

Por supuesto, en una consulta real normalmente habría algunos filtros, etc.

El zip de Python combinado con el operador de expansión en línea * es una solución muy útil para esto:

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

Entonces solo tienes que [0] indexar una vez. Para una lista tan corta tu comprensión es más rápida:

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

Sin embargo, para listas más largas, el zip debería ser más rápido:

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

Así que depende de usted determinar cuál es mejor para su situación.

Una forma de disminuir el desorden en la fuente es iterar así:

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

Aunque esta solución es un personaje más largo que usar el operador [] , creo que es más fácil para la vista.

Para menos desorden, quite el paréntesis. Esto hace que sea más difícil al leer el código, notar que en realidad estás manejando tuplas:

 results = [r for r, in results] 

Luché con esto también hasta que me di cuenta de que es como cualquier otra consulta:

 for result in results: print result.column_name 

Encontré lo siguiente más legible , también incluye la respuesta para el dict (en Python 2.7):

 d = {id_: name for id_, name in session.query(Customer.id, Customer.name).all()} l = [r.id for r in session.query(Customer).all()] 

Para el valor único, pedir prestado de otra respuesta:

 l = [name for (name, ) in session.query(Customer.name).all()] 

Compare con la solución zip incorporada, adaptada a la lista:

 l = list(zip(*session.query(Customer.id).all())[0]) 

que en mi tiempo proporciona solo un 4% de mejoras de velocidad.

Mi solución se ve así;)

 def column(self): for column, *_ in Model.query.with_entities(Model.column).all(): yield column 

NOTA: sólo py3.

Wow, chicos, ¿por qué la tensión? Hay un método más empinado, más rápido y más elegante.

 >>> results = [('result',), ('result_2',), ('result_3',)] >>> sum(results, tuple()) ('result', 'result_2', 'result_3') 

Velocidad:

 >>> timeit('result = zip(*[("result",), ("result_2",), ("result_3",)])', number=10000) 0.004222994000883773 >>> timeit('result = sum([("result",), ("result_2",), ("result_3",)], ())', number=10000) 0.0038205889868550003 

Pero si hay más elementos en la lista , usa solo zip . Zip más velocidad.