Como executar SQL bruto no aplicativo Flask-SQLAlchemy


219

Como você executa SQL bruto no SQLAlchemy?

Eu tenho um aplicativo da web python que é executado no balão e faz interface com o banco de dados por meio do SQLAlchemy.

Eu preciso de uma maneira de executar o SQL bruto. A consulta envolve várias junções de tabela junto com exibições embutidas.

Eu tentei:

connection = db.session.connection()
connection.execute( <sql here> )

Mas continuo recebendo erros de gateway.


5
Já vi isso antes, mas não consegui encontrar um tutorial sobre como executar uma atualização. Também prefiro não aprender a sintaxe e ocultar uma consulta SQL bastante longa (cerca de 20 linhas).
starwing123

103
@MarkusUnterwaditzer Eu costumava pensar isso, mas agora discordo totalmente. O SQL bruto e adequadamente parametrizado é geralmente muito mais fácil de ler e manter do que várias chamadas de função e objetos que o geram. Ele também oferece todos os recursos do banco de dados sem ter que passar por obstáculos para fazer com que o ORM gere a sintaxe correta (se for possível) e evita que o ORM faça coisas inesperadas. Você pode fazer a pergunta: "Então, por que usar o SQLAlchemy?", E a única resposta que tenho é: "O aplicativo existente o usa e alterar tudo é muito caro".
Jpmc26

4
@ jpmc26 Atualizei seu comentário - como um amante do SQL, tenho dificuldade com a idéia de "entregar as chaves do banco de dados" a um alquimista irresponsável e tendem a inclinar-se ao lado do ORM é um antipadrão :) disse que gostaria de acelerar certos componentes, como registro / gerenciamento de usuários, e também a geração de tabelas com sequências de botões para as quais eu posso codificar as ações + SQL. Você se deparou com algumas ferramentas amigáveis ​​ao cético para ORM que funcionam bem para você em uma estrutura Python?
Zx81 17/07/2015

@ jpmc26 O que você usa em uma estrutura Python para usar apenas SQL ou bem parecido com o C # Dapper? Tudo o que vejo em uma estrutura da Web Python quer que eu use o SQLAlchemy, e não gosto de um ORM, e se eu usar um, é extremamente mínimo.
johnny

@ johnny Não tive a oportunidade de tentar eu mesmo, mas as bibliotecas de conexão de banco de dados brutas provavelmente são suficientes. Por exemplo, o psycopg2 possui cursores que retornam namedtuplee dictdiretamente: initd.org/psycopg/docs/extras.html .
Jpmc26

Respostas:


310

Você tentou:

result = db.engine.execute("<sql here>")

ou:

from sqlalchemy import text

sql = text('select name from penguins')
result = db.engine.execute(sql)
names = [row[0] for row in result]
print names

7
Se você faz uma inserção ou atualização, como confirma a transação?
David S

14
Se você estiver usando SQL bruto, controla as transações, portanto, você mesmo deve emitir as instruções BEGINe COMMIT.
Miguel

1
Os mesmos comandos SQL funcionam quando você os emite sem SQLAlchemy? Convém ativar a depuração no banco de dados para poder ver quais comandos ele está executando.
Miguel

27
db.engine.execute(text("<sql here>")).execution_options(autocommit=True))executa e confirma também.
Devi

8
@ Miguel "Se você estiver usando SQL bruto, controla as transações, portanto, você deve emitir as instruções BEGIN e COMMIT." Isto simplesmente não é verdade. Você pode usar o SQL bruto com um objeto de sessão. Acabei de notar esse comentário, mas você pode ver minha resposta sobre como usar uma sessão com SQL bruto.
precisa saber é o seguinte

180

Os objetos de sessão do SQL Alchemy têm seu próprio executemétodo:

result = db.session.execute('SELECT * FROM my_table WHERE my_column = :val', {'val': 5})

Todas as suas consultas de aplicativos devem passar por um objeto de sessão, sejam eles SQL brutos ou não. Isso garante que as consultas sejam gerenciadas adequadamente por uma transação , o que permite que várias consultas na mesma solicitação sejam confirmadas ou revertidas como uma única unidade. Sair da transação usando o mecanismo ou a conexão oferece um risco muito maior de sutis, possivelmente difíceis de detectar bugs que podem deixar dados corrompidos. Cada solicitação deve ser associada a apenas uma transação e usardb.session garantirá que este é o caso do seu aplicativo.

Observe também que executefoi projetado para consultas parametrizadas . Use parâmetros, como :valno exemplo, para quaisquer entradas na consulta para se proteger de ataques de injeção de SQL. Você pode fornecer o valor para esses parâmetros passando umdict como o segundo argumento, em que cada chave é o nome do parâmetro, conforme aparece na consulta. A sintaxe exata do próprio parâmetro pode ser diferente dependendo do seu banco de dados, mas todos os principais bancos de dados relacionais os suportam de alguma forma.

Supondo que é um SELECT consulta, isso retornará uma iterável deRowProxy objetos.

Você pode acessar colunas individuais com uma variedade de técnicas:

for r in result:
    print(r[0]) # Access by positional index
    print(r['my_column']) # Access by column name as a string
    r_dict = dict(r.items()) # convert to dict keyed by column names

Pessoalmente, prefiro converter os resultados em namedtuple s:

from collections import namedtuple

Record = namedtuple('Record', result.keys())
records = [Record(*r) for r in result.fetchall()]
for r in records:
    print(r.my_column)
    print(r)

Se você não estiver usando a extensão Flask-SQLAlchemy, ainda poderá usar facilmente uma sessão:

import sqlalchemy
from sqlalchemy.orm import sessionmaker, scoped_session

engine = sqlalchemy.create_engine('my connection string')
Session = scoped_session(sessionmaker(bind=engine))

s = Session()
result = s.execute('SELECT * FROM my_table WHERE my_column = :val', {'val': 5})

Um Select retornará um ResultProxy.
Alan B

@AlanB Sim. Escolhi mal minhas palavras quando a chamei de sequência, implicando que ela implementa o protocolo de sequência. Eu corrigi e esclareci. Obrigado.
Jpmc26 6/08/19

@ jpmc26 deve fechar a sessão após executar a consulta como db.session.close ()? E ainda terá os benefícios do pool de conexões?
ravi malhotra 17/03

58

docs: Tutorial da linguagem de expressão SQL - Usando texto

exemplo:

from sqlalchemy.sql import text

connection = engine.connect()

# recommended
cmd = 'select * from Employees where EmployeeGroup = :group'
employeeGroup = 'Staff'
employees = connection.execute(text(cmd), group = employeeGroup)

# or - wee more difficult to interpret the command
employeeGroup = 'Staff'
employees = connection.execute(
                  text('select * from Employees where EmployeeGroup = :group'), 
                  group = employeeGroup)

# or - notice the requirement to quote 'Staff'
employees = connection.execute(
                  text("select * from Employees where EmployeeGroup = 'Staff'"))


for employee in employees: logger.debug(employee)
# output
(0, 'Tim', 'Gurra', 'Staff', '991-509-9284')
(1, 'Jim', 'Carey', 'Staff', '832-252-1910')
(2, 'Lee', 'Asher', 'Staff', '897-747-1564')
(3, 'Ben', 'Hayes', 'Staff', '584-255-2631')

1
O link para os documentos sqlalchemy parece estar desatualizado. Esta é mais recente: docs.sqlalchemy.org/en/latest/core/...
Carl

1
Posso perguntar por que estamos usando ==?
Nam G VU

1
@Jake Berger, um grande obrigado por você. Eu perdi quase um dia em busca desta resposta. Eu estava apenas executando diretamente o sql sem converter em texto. Estava lançando erro sempre que temos% student% na minha cláusula where. Um grande aplauso por sua resposta.
precisa saber é o seguinte

1
@NamGVU porque, como na maioria das linguagens de programação, =normalmente é reservado para atribuir um valor; enquanto que ==é reservado para comparar valores
Jake Berger

2
@JakeBerger Você tem um link para isso? O SQL não é essa linguagem e, a julgar pelos documentos do SQLAlchemy, não é assim.
Johndodo 21/10/19

36

Você pode obter os resultados das consultas SELECT SQL usando from_statement()e text()como mostrado aqui . Você não precisa lidar com tuplas dessa maneira. Como exemplo de uma classe Usercom o nome da tabela, usersvocê pode tentar,

from sqlalchemy.sql import text
.
.
.
user = session.query(User).from_statement(
    text("SELECT * FROM users where name=:name")).\
    params(name='ed').all()

return user

15
result = db.engine.execute(text("<sql here>"))

executa o, <sql here>mas não o confirma, a menos que você estejaautocommit modo. Portanto, inserções e atualizações não refletiriam no banco de dados.

Para confirmar após as alterações, faça

result = db.engine.execute(text("<sql here>").execution_options(autocommit=True))

2

Esta é uma resposta simplificada de como executar a consulta SQL a partir do Flask Shell

Primeiro, mapeie seu módulo (se seu módulo / aplicativo estiver manage.py na pasta principal e você estiver em um sistema operacional UNIX), execute:

export FLASK_APP=manage

Executar shell do balão

flask shell

Importe o que precisamos:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy(app)
from sqlalchemy import text

Execute sua consulta:

result = db.engine.execute(text("<sql here>").execution_options(autocommit=True))

Isso usa a conexão de banco de dados atualmente que possui o aplicativo.


0

Você já tentou usar connection.execute(text( <sql here> ), <bind params here> )e vincular parâmetros, conforme descrito nos documentos ? Isso pode ajudar a resolver muitos problemas de formatação e desempenho de parâmetros. Talvez o erro do gateway seja um tempo limite? Os parâmetros de ligação tendem a fazer consultas complexas serem executadas substancialmente mais rapidamente.


2
de acordo com documentos , deve ser connection.execute(text(<sql here>), <bind params> ). bind paramsnão deve estar em text(). alimentando nos parâmetros de ligação para o método de execução ()
Jake Berger

O link de Jake está quebrado. Eu acho que este é o URL que é relevante agora: docs.sqlalchemy.org/en/latest/core/…
code_dredd

-1

Se você deseja evitar tuplas, outra maneira é chamar os métodos first, oneou all:

query = db.engine.execute("SELECT * FROM blogs "
                           "WHERE id = 1 ")

assert query.first().name == "Welcome to my blog"
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.