77 votes

SQL Alchemy ORM retournant une seule colonne, comment éviter le post traitement commun

J'utilise l'ORM de SQL Alchemy et je constate que lorsque je renvoie une seule colonne, j'obtiens les résultats suivants :

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

Avec un ensemble comme celui-ci, je trouve que je dois le faire souvent :

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

Ce n'est pas si "mauvais" car mes ensembles de résultats sont généralement petits, mais s'ils ne l'étaient pas, cela pourrait ajouter des frais généraux importants. Le plus gros problème est que j'ai l'impression que cela encombre la source, et manquer cette étape est une erreur assez courante que je rencontre.

Existe-t-il un moyen d'éviter cette étape supplémentaire ?

Une parenthèse connexe : Ce comportement de l'orm semble peu pratique dans ce cas, mais dans un autre cas où mon ensemble de résultats était, [(id, valeur)], il se termine comme ceci :

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

Je peux alors juste faire :

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

Celle-ci a l'avantage d'avoir un sens en tant qu'étape utile après le retour des résultats.

Est-ce vraiment un problème ou est-ce que je suis juste un pinailleur et le post-traitement après avoir obtenu l'ensemble des résultats est logique dans les deux cas ? Je suis sûr que nous pouvons penser à d'autres opérations courantes de post-traitement pour rendre l'ensemble des résultats plus utilisable dans le code de l'application. Existe-t-il des solutions performantes et pratiques dans tous les cas ou le post-traitement est-il inévitable et simplement nécessaire pour des usages applicatifs différents ?

Lorsque mon application peut réellement tirer parti des objets renvoyés par l'ORM de SQL Alchemy, il semble extrêmement utile, mais dans les cas où je ne peux pas ou ne veux pas, pas tellement. S'agit-il d'un problème commun aux ORM en général ? Est-il préférable de ne pas utiliser la couche ORM dans des cas comme celui-ci ?

Je suppose que je devrais montrer un exemple des requêtes orm dont je parle :

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

ou

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

Bien sûr, dans une requête réelle, il y a normalement des filtres, etc.

26voto

Dag Høidahl Points 923

Une façon de réduire le désordre dans la source est d'itérer comme ceci :

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

Bien que cette solution soit plus longue d'un caractère que l'utilisation de la touche [] opérateur, je pense que c'est plus facile pour les yeux.

Pour encore moins d'encombrement, supprimez les parenthèses. Cela rend plus difficile, en lisant le code, de remarquer que vous manipulez en fait des tuples :

results = [r for r, in results]

21voto

Beright Points 129

Le zip de Python combiné à l'opérateur d'expansion en ligne * est une solution assez pratique pour cela :

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

Vous n'aurez alors à indexer [0] qu'une seule fois. Pour une liste aussi courte, votre compréhension est plus rapide :

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

Cependant, pour les listes plus longues, zip devrait être plus rapide :

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

C'est donc à vous de déterminer ce qui convient le mieux à votre situation.

1 votes

L'hypothèse ici est qu'il pourrait y avoir une million dans la liste et soudain, cela ne semble pas très intelligent.

17voto

Pakman Points 825

J'ai eu du mal avec ça aussi jusqu'à ce que je réalise que c'est comme n'importe quelle autre requête :

for result in results:
     print result.column_name

0 votes

Oui, je suis d'accord, les NamedTuples ne sont pas immédiatement évidents. Mon post-traitement le plus courant consiste toujours à créer un dictionnaire de quelque sorte. La plupart de mes post-traitements ont été éliminés par une meilleure architecture de base de données et l'utilisation de SQLAlchemy, de sorte que j'ai les valeurs dont j'ai besoin attachées à des objets ORM, ou disponibles par le biais de méthodes.

2voto

Roman Susi Points 1073

J'ai trouvé ce qui suit plus lisible comprend également la réponse pour le dict (dans 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()]

Pour la valeur unique, emprunt d'une autre réponse :

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

Comparez avec le système intégré zip solution, adaptée à la liste :

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

qui, dans mon temps, ne fournit qu'environ 4% d'amélioration de la vitesse.

0voto

brunsgaard Points 4071

Ma solution ressemble à ceci ;)

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

NOTE : py3 seulement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X