Eu tenho um objeto de consulta SQLAlchemy e desejo obter o texto da instrução SQL compilada, com todos os seus parâmetros vinculados (por exemplo, nenhuma %s
ou outras variáveis esperando para serem vinculadas pelo compilador de instrução ou mecanismo de dialeto MySQLdb, etc).
Chamar str()
a consulta revela algo assim:
SELECT id WHERE date_added <= %s AND date_added >= %s ORDER BY count DESC
Tentei procurar em query._params, mas é um dicionário vazio. Eu escrevi meu próprio compilador usando este exemplo do sqlalchemy.ext.compiler.compiles
decorador, mas mesmo a instrução lá ainda tem %s
onde eu quero os dados.
Não consigo descobrir quando meus parâmetros se misturam para criar a consulta; ao examinar o objeto de consulta, eles são sempre um dicionário vazio (embora a consulta execute bem e o mecanismo a imprima quando você ativa o log de eco).
Estou começando a receber a mensagem de que SQLAlchemy não quer que eu saiba a consulta subjacente, já que quebra a natureza geral da interface da API de expressão em todos os DB-APIs diferentes. Não me importo se a consulta for executada antes de eu descobrir o que era; Eu só quero saber!
fonte
c = q.statement.compile(...)
, você pode apenas obterc.params
as_scalar()
-método deQuery
.str(q)
.A documentação usa
literal_binds
para imprimir uma consultaq
incluindo parâmetros:A documentação também emite este aviso:
fonte
Isso deve funcionar com Sqlalchemy> = 0,6
fonte
adapt
desta maneira. No mínimo, chame prepare () no valor de retorno dele a cada vez, fornecendo a conexão como um argumento, para que possa fazer aspas adequadas.prepare
no valor de retorno, mas é parece que ele não tem esse método:AttributeError: 'psycopg2._psycopg.AsIs' object has no attribute 'prepare'
. Estou usando o psycopg2 2.2.1 BTWPara o back-end do MySQLdb, modifiquei um pouco a resposta incrível de albertov (muito obrigado!). Tenho certeza de que eles poderiam ser mesclados para verificar se
comp.positional
estavam,True
mas isso está um pouco além do escopo desta questão.fonte
return tuple(params)
funcionar perfeitamente ! Você me poupou incontáveis horas de ter que percorrer um caminho extremamente doloroso.O fato é que o sqlalchemy nunca mistura os dados com sua consulta. A consulta e os dados são passados separadamente para o driver de banco de dados subjacente - a interpolação de dados acontece em seu banco de dados.
O Sqlalchemy passa a consulta como você viu no
str(myquery)
banco de dados e os valores irão em uma tupla separada.Você poderia usar alguma abordagem em que interpola os dados com a consulta você mesmo (como albertov sugerido abaixo), mas isso não é a mesma coisa que o sqlalchemy está executando.
fonte
SELECT id WHERE date_added <= %s AND date_added >= %s ORDER BY count DESC
É a pergunta final. Eles%s
são enviados para o banco de dados por sqlalchemy - sqlalchemy NUNCA coloca os dados reais no lugar de% ssqlalchemy.dialects.mysql.mysqldb
,do_executemany()
passa a instrução e os parâmetros separadamente para o cursor MySQLdb. yay indireção!Primeiro, deixe-me começar dizendo que presumo que você esteja fazendo isso principalmente para fins de depuração - eu não recomendaria tentar modificar a instrução fora da API fluente do SQLAlchemy.
Infelizmente, não parece haver uma maneira simples de mostrar a instrução compilada com os parâmetros de consulta incluídos. SQLAlchemy na verdade não coloca os parâmetros na instrução - eles são passados para o mecanismo de banco de dados como um dicionário . Isso permite que a biblioteca específica do banco de dados controle coisas como o escape de caracteres especiais para evitar injeção de SQL.
Mas você pode fazer isso em um processo de duas etapas com razoável facilidade. Para obter o extrato, você pode fazer como já mostrou e apenas imprimir a consulta:
Você pode chegar um passo mais perto com query.statement, para ver os nomes dos parâmetros. Observe
:id_1
abaixo vs%s
acima - não é realmente um problema neste exemplo muito simples, mas pode ser a chave em uma declaração mais complicada.Então, você pode obter os valores reais dos parâmetros obtendo a
params
propriedade da instrução compilada:Isso funcionou para um back-end MySQL, pelo menos; Eu esperaria que também fosse geral o suficiente para PostgreSQL sem a necessidade de usar
psycopg2
.fonte
Para o backend postgresql usando psycopg2, você pode ouvir o
do_execute
evento e, em seguida, usar o cursor, a instrução e os parâmetros do tipo coagidos junto comCursor.mogrify()
os parâmetros embutidos. Você pode retornar True para evitar a execução real da consulta.Uso de amostra:
fonte
A solução a seguir usa SQLAlchemy Expression Language e funciona com SQLAlchemy 1.1. Esta solução não mistura os parâmetros com a consulta (conforme solicitado pelo autor original), mas fornece uma maneira de usar modelos SQLAlchemy para gerar strings de consulta SQL e dicionários de parâmetro para diferentes dialetos SQL. O exemplo é baseado no tutorial http://docs.sqlalchemy.org/en/rel_1_0/core/tutorial.html
Dada a aula,
podemos produzir uma instrução de consulta usando a função select .
Em seguida, podemos compilar a instrução em um objeto de consulta.
Por padrão, a instrução é compilada usando uma implementação 'nomeada' básica que é compatível com bancos de dados SQL como SQLite e Oracle. Se você precisa especificar um dialeto como PostgreSQL, você pode fazer
Ou se você quiser especificar explicitamente o dialeto como SQLite, você pode alterar o estilo de parâmetro de 'qmark' para 'nomeado'.
Do objeto de consulta, podemos extrair a string de consulta e os parâmetros de consulta
e finalmente execute a consulta.
fonte
Você pode usar eventos da família ConnectionEvents :
after_cursor_execute
oubefore_cursor_execute
.Em sqlalchemy UsageRecipes de @zzzeek você pode encontrar este exemplo:
Aqui você pode obter acesso ao seu extrato
fonte
Então, juntando um monte de pequenos pedaços dessas diferentes respostas, eu vim com o que eu precisava: um conjunto simples de código para inserir e ocasionalmente, mas de forma confiável (ou seja, lida com todos os tipos de dados), pegue o SQL compilado exato enviado para o meu Back-end do Postgres apenas interrogando a própria consulta:
fonte
Acho que .statement possivelmente resolveria o problema: http://docs.sqlalchemy.org/en/latest/orm/query.html?highlight=query
fonte