Mensagem de erro estranha de SQLAlchemy: TypeError: o objeto 'dict' não oferece suporte à indexação

145

Estou usando o SQL artesanal para buscar dados de um banco de dados PG, usando SqlAlchemy. Estou tentando uma consulta que contém o SQL como operador '%' e que parece lançar SqlAlcjhemy através de um loop:

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

Alguém sabe o que está causando essa mensagem de erro enganosa e como posso corrigi-la?

[[Editar]]

Antes que alguém pergunte, não há nada de especial ou sofisticado nas funções incluídas acima. Por exemplo, a função executeSql () chama simplesmente conn.execute (sql) e retorna os resultados. A variável conn é simplesmente a conexão estabelecida anteriormente com o banco de dados.

Homunculus Reticulli
fonte
você pode postar o código de executeSql(...)? E também, você realmente tem RETURNING *na SELECTdeclaração?
van
@van Eu perdi essa. Não há 'RETURNING *' no SQL que está causando o problema. Eu vou corrigir a pergunta.
Homunculus Reticulli
1
esta resposta [ stackoverflow.com/questions/3944276/… é útil?
van
2
@van: Obrigado !. sim. Eu tive que usar '\ %%' em vez de '%'. A instrução está corretamente executada agora.
Homunculus Reticulli
3
ótimo. poste uma resposta curta (e aceite-a) que funcionou para você em prol da integridade.
van

Respostas:

227

Você tem que dar %%para usá-lo como %porque %em python é usado como formatação de string, então quando você escreve single, %assume que você irá substituir algum valor por isso.

Então, quando você quiser colocar uma única %string com a consulta sempre, coloque double %.

Nilesh
fonte
27
Eu gostaria que eles de atualizado que mensagem de erro, cada vez que eu obtê-lo eu acabar pouso nessa página e resposta
oshi2016
86

SQLAlchemy tem uma text()função para agrupar texto que parece escapar corretamente do SQL para você.

Ou seja,

res = executeSql(sqlalchemy.text(sql))

deve funcionar para você e evitar que você faça o escape manual.

Ilja Everilä
fonte
13
Essa deve ser a resposta selecionada. Resolveu o problema no meu caso.
Gani Simsek
1
Observe que isso não escapa aos comentários, mas, caso contrário, é uma solução fantástica.
ClimbsRocks
Isso funcionou para mim, e era mais fácil de implementar do que mudar todas as nossas consultas com duplo%
Philippe Oger
6

Não consigo encontrar o "executeSql" nos documentos sqlalchemy versão 1.2 , mas a linha abaixo funcionou para mim

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

Parece que seu problema pode estar relacionado a esse bug .

Nesse caso, você deve escapar três vezes como solução alternativa.

Brian Cain
fonte
2

Encontrei mais um caso quando esse erro aparece:

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

Em outras palavras, se você fornecer o parâmetro ( %s) na consulta, mas esquecer de adicionar parâmetros de consulta. Nesse caso, a mensagem de erro é muito enganadora.

Tupteq
fonte
1

Mais uma %observação - você também deve escapar (ou excluir) os caracteres nos comentários. Infelizmente, sqlalchemy.text(query_string)não escapa dos sinais de porcentagem nos comentários.

Escaladas
fonte
1

Outra maneira de resolver seu problema, se você não quiser escapar de %caracteres ou usar sqlalchemy.text(), é usar uma expressão regular.

Ao invés de:

select id from ref_geog where short_name LIKE '%opt'

Tente (para correspondência que diferencia maiúsculas de minúsculas):

select id from ref_geog where short_name ~ 'opt$' 

ou (para não diferenciar maiúsculas de minúsculas):

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

Ambos LIKEe regex são abordados na documentação sobre correspondência de padrões .

Observe que:

Diferente dos padrões LIKE, uma expressão regular pode corresponder a qualquer lugar dentro de uma sequência, a menos que a expressão regular seja explicitamente ancorada ao início ou ao final da sequência.

Para uma âncora, você pode usar a asserção $para final de sequência (ou ^para início).

C8H10N4O2
fonte
0

Isso também pode resultar do caso - caso os parâmetros a serem passados ​​para o SQL sejam declarados no formato DICT e estejam sendo manipulados no SQL na forma de LIST ou TUPPLE.

Sonar Pralhad Narsinh
fonte