Message d'erreur étrange SQLAlchemy: TypeError: l'objet 'dict' ne prend pas en charge l'indexation

145

J'utilise SQL fabriqué à la main pour récupérer des données à partir d'une base de données PG, en utilisant SqlAlchemy. J'essaie une requête qui contient le SQL comme l'opérateur '%' et qui semble lancer SqlAlcjhemy à travers une boucle:

sql = """
       SELECT DISTINCT u.name from user u
        INNER JOIN city c ON u.city_id = c.id
        WHERE c.designation=upper('fantasy') 
        AND c.id IN (select id from ref_geog where short_name LIKE '%opt')
      """

# The last line in the above statement throws the error mentioned in the title. 
# However if the last line is change to:
# AND c.id IN (select id from ref_geog where short_name = 'helloopt')
# the script runs correctly.
#
# I also tried double escaping the '%' i.e. using '%%' instead - that generated the same error as previously.

connectDb()
res = executeSql(sql)
print res
closeDbConnection()

Quelqu'un sait ce qui cause ce message d'erreur trompeur et comment je peux le réparer?

[[Éditer]]

Avant que quiconque ne demande, il n'y a rien de spécial ou de fantaisie dans les fonctions incluses ci-dessus. Par exemple, la fonction executeSql () appelle simplement conn.execute (sql) et renvoie les résultats. La variable conn est simplement la connexion précédemment établie à la base de données.

Homunculus reticulli
la source
pouvez-vous poster le code de executeSql(...)? Et aussi, avez-vous vraiment RETURNING *dans la SELECTdéclaration?
van
@van j'ai raté celui-là. Il n'y a pas de «RETURNING *» dans le SQL qui cause le problème. Je vais corriger la question.
Homunculus Reticulli
1
cette réponse [ stackoverflow.com/questions/3944276/… est-elle utile?
van
2
@van: Merci !. Oui. J'ai dû utiliser '\ %%' au lieu de '%'. L'instruction est maintenant correctement exécutée.
Homunculus Reticulli
3
génial. veuillez poster une réponse courte (et l'accepter) qui a fonctionné pour vous par souci d'exhaustivité.
van

Réponses:

228

Vous devez donner %%pour l'utiliser %car %en python est utilisé comme formatage de chaîne, donc lorsque vous écrivez un single, %il suppose que vous allez remplacer une valeur par ceci.

Ainsi, lorsque vous souhaitez placer un simple %dans une chaîne avec une requête, placez toujours le double %.

Nilesh
la source
27
Je souhaite qu'ils mettent à jour ce message d'erreur, chaque fois que je l'obtiens, je
finis
86

SQLAlchemy a une text()fonction pour envelopper le texte qui semble échapper correctement au SQL pour vous.

C'est à dire

res = executeSql(sqlalchemy.text(sql))

devrait fonctionner pour vous et vous éviter d'avoir à faire l'échappement manuel.

Ilja Everilä
la source
13
Cela devrait être la réponse choisie. Cela a résolu le problème dans mon cas.
Gani Simsek
1
Notez que cela n'échappe pas aux commentaires, mais sinon, c'est une solution fantastique.
ClimbsRocks
Cela a fonctionné pour moi et était plus facile à mettre en œuvre que de modifier toutes nos requêtes avec un double%
Philippe Oger
6

Je ne trouve pas le "executeSql" dans la documentation de sqlalchemy version 1.2 , mais la ligne ci-dessous a fonctionné pour moi

engine.execute(sqlalchemy.text(sql_query))
Chandra Prakash Dixit
la source
4

Il semble que votre problème soit lié à ce bogue .

Dans ce cas, vous devez triple-échapper pour contourner le problème.

Brian Cain
la source
2

J'ai trouvé un autre cas lorsque cette erreur apparaît:

c.execute("SELECT * FROM t WHERE a = %s")

En d'autres termes, si vous fournissez le paramètre ( %s) dans la requête, mais que vous oubliez d'ajouter des paramètres de requête. Dans ce cas, le message d'erreur est très trompeur.

Tupteq
la source
1

Une dernière remarque: vous devez également échapper (ou supprimer) des %caractères dans les commentaires. Malheureusement, sqlalchemy.text(query_string)n'échappe pas aux signes de pourcentage dans les commentaires.

EscaladeRoches
la source
1

Une autre façon de résoudre votre problème, si vous ne voulez pas échapper %ou utiliser des caractères sqlalchemy.text(), est d'utiliser une expression régulière.

Au lieu de:

select id from ref_geog where short_name LIKE '%opt'

Essayez (pour une correspondance sensible à la casse):

select id from ref_geog where short_name ~ 'opt$' 

ou (pour insensible à la casse):

select id from ref_geog where short_name ~* 'opt$'

Les deux LIKEet regex sont traités dans la documentation sur la correspondance de modèles .

Notez que:

Contrairement aux modèles LIKE, une expression régulière est autorisée à correspondre n'importe où dans une chaîne, sauf si l'expression régulière est explicitement ancrée au début ou à la fin de la chaîne.

Pour une ancre, vous pouvez utiliser l'assertion $pour la fin de la chaîne (ou ^pour le début).

C8H10N4O2
la source
0

Cela pourrait également résulter du cas - dans le cas où les paramètres à passer au SQL sont déclarés au format DICT et sont manipulés dans le SQL sous la forme de LISTE ou TUPPLE.

Sonar Pralhad Narsinh
la source