Flask-SQLAlchemy 앱에서 원시 SQL을 실행하는 방법


219

SQLAlchemy에서 원시 SQL을 어떻게 실행합니까?

플라스크에서 실행되고 SQLAlchemy를 통해 데이터베이스에 인터페이스하는 Python 웹 앱이 있습니다.

원시 SQL을 실행하는 방법이 필요합니다. 쿼리에는 인라인 뷰와 함께 여러 테이블 조인이 포함됩니다.

난 노력 했어:

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

그러나 게이트웨이 오류가 계속 발생합니다.


5
이전에 살펴 봤지만 업데이트 실행에 대한 자습서를 찾을 수 없습니다. 또한 구문을 배우지 않고 다소 긴 (약 20 줄) SQL 쿼리를 은닉합니다.
starwing123

103
@MarkusUnterwaditzer 나는 그렇게 생각했지만 지금은 매우 동의하지 않습니다. 적절하고 매개 변수화 된 원시 SQL은 일반적으로 함수 호출 및이를 생성하는 오브젝트보다 훨씬 읽고 이해하기 쉽습니다. 또한 ORM이 올바른 구문을 생성하도록 (가능한 경우) 후프를 건너 뛰지 않고도 데이터베이스의 전체 기능을 제공하고 ORM이 예기치 않은 작업을 수행하지 못하게합니다. "SQLAlchemy를 사용하는 이유는 무엇입니까?"라는 질문을 할 수 있으며 "기존 응용 프로그램에서이를 사용하고 모든 것을 변경하는 것이 너무 비쌉니다."
jpmc26

4
@ jpmc26 올렸 귀하의 의견은-로 SQL의 연인, 내가 무책임한 연금술사에 "데이터베이스에 키를 포기"와 측면에 의지하는 경향이의 생각에 힘든 시간이 ORM은 안티 패턴입니다 :) 그 존재 나는 사용자 등록 / 관리와 같은 특정 구성 요소와 액션 + SQL을 코딩 할 수있는 버튼 시퀀스가있는 테이블 생성을 가속화하기를 원한다고 말했다. Python 프레임 워크에서 잘 작동하는 ORM 회의론자들에게 친숙한 도구를 사용해 보셨습니까?
zx81

@ jpmc26 파이썬 프레임 워크에서 SQL을 사용하거나 C # Dapper와 거의 비슷하게 사용하는 것은 무엇입니까? 파이썬 웹 프레임 워크에서 볼 수있는 모든 것은 SQLAlchemy를 사용하기를 원하며 ORM을 좋아하지 않으며 ORM을 사용하면 극도로 최소화됩니다.
johnny

@ johnny 직접 시도해 볼 기회는 없었지만 원시 데이터베이스 연결 라이브러리로 충분할 것입니다. 예를 들어, psycopg2는 커서 복귀가 namedtuple와가 dict: 직접 initd.org/psycopg/docs/extras.html를 .
jpmc26

답변:


310

시도해 보셨습니까?

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

또는:

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
삽입 또는 업데이트를 수행하면 트랜잭션을 어떻게 커밋합니까?
David S

14
원시 SQL을 사용하는 경우 트랜잭션을 제어하므로 BEGINand COMMIT문을 직접 발행해야합니다 .
Miguel

1
SQLAlchemy없이 실행할 때 동일한 SQL 명령이 작동합니까? 데이터베이스에서 디버깅을 활성화하여 실행중인 명령을 확인할 수 있습니다.
Miguel

27
db.engine.execute(text("<sql here>")).execution_options(autocommit=True))실행하고 커밋합니다.
데비

8
@Miguel "원시 SQL을 사용하는 경우 트랜잭션을 제어하므로 BEGIN 및 COMMIT 문을 직접 발행해야합니다." 이것은 단순히 사실이 아닙니다. 원시 SQL을 세션 오브젝트와 함께 사용할 수 있습니다. 이 의견을 방금 주목했지만 원시 SQL에서 세션을 사용하는 방법에 대한 나의 대답을 볼 수 있습니다.
jpmc26

180

SQL Alchemy 세션 개체에는 고유 한 execute방법이 있습니다.

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

모든 응용 프로그램 쿼리는 원시 SQL인지 여부에 관계없이 세션 개체를 통과해야합니다. 이를 통해 트랜잭션 이 쿼리를 올바르게 관리 할 수 있으므로 동일한 요청의 여러 쿼리를 단일 단위로 커밋하거나 롤백 할 수 있습니다. 엔진 이나 연결 을 사용하여 트랜잭션 외부로 나가면 미묘한 위험에 노출되어 데이터가 손상 될 수있는 버그를 감지하기 어려울 수 있습니다. 각 요청은 하나의 트랜잭션에만 연결되어야하며이를 사용 db.session하면 애플리케이션에 해당하는 것입니다.

또한 매개 변수화 된 쿼리를execute 위해 설계되었습니다 . SQL 인젝션 공격으로부터 자신을 보호하기 위해 쿼리에 입력 할 때 예제 와 같이 매개 변수를 사용하십시오 . a 를 두 번째 인수로 전달하여 이러한 매개 변수의 값을 제공 할 수 있습니다 . 여기서 각 키는 조회에 표시되는 매개 변수의 이름입니다. 매개 변수 자체의 정확한 구문은 데이터베이스에 따라 다를 수 있지만 모든 주요 관계형 데이터베이스는이를 특정 형식으로 지원합니다.:valdict

그것의 가정 SELECT쿼리,이 반환됩니다 반복 가능RowProxy객체.

다양한 기술로 개별 열에 액세스 할 수 있습니다.

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

개인적으로 결과를 namedtuples 로 변환하는 것을 선호합니다 .

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)

Flask-SQLAlchemy 확장을 사용하지 않는 경우에도 세션을 쉽게 사용할 수 있습니다.

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

Select는 ResultProxy를 반환합니다.
Alan B

@AlanB 예. 나는 그것을 시퀀스라고 불렀을 때 내 말을 잘못 선택했다. 이는 시퀀스 프로토콜을 구현한다는 것을 암시한다. 나는 수정하고 명확히했다. 감사.
jpmc26

@ jpmc26은 db.session.close ()와 같은 쿼리를 실행 한 후 세션을 닫아야합니까? 그리고 여전히 연결 풀링의 이점이 있습니까?
ravi malhotra

58

docs : SQL 식 언어 자습서-텍스트 사용

예:

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
sqlalchemy 문서에 대한 링크가 오래되었습니다. 더 최근입니다 : docs.sqlalchemy.org/en/latest/core/…
Carl

1
왜 사용하는지 물어봐도 ==될까요?
Nam G VU

1
@Jake Berger에게 감사합니다. 나는이 답변을 찾기 위해 거의 하루를 낭비했습니다. 텍스트로 변환하지 않고 SQL을 직접 실행했습니다. where 절에 % students %가있을 때마다 오류가 발생했습니다. 귀하의 답변에 큰 박수를 보냅니다.
Suresh Kumar

1
@NamGVU는 대부분의 프로그래밍 언어와 마찬가지로 =일반적으로 값 을 할당 하기 위해 예약되어 있습니다 . 반면 ==예약되어 비교
제이크 버거

2
@JakeBerger 당신은 그것에 대한 링크가 있습니까? SQL은 그런 언어가 아니며 SQLAlchemy 문서에 의해 판단되는 것은 아닙니다.
johndodo

36

from_statement()여기에text() 표시된 것처럼 SELECT SQL 쿼리 결과를 얻을 수 있습니다 . 이 방법으로 튜플을 다룰 필요가 없습니다. 시도 할 수 있는 테이블 이름 을 가진 클래스의 예로 ,Userusers

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

실행 <sql here>하지만 autocommit모드 에 있지 않으면 커밋하지 않습니다 . 따라서 삽입 및 업데이트는 데이터베이스에 반영되지 않습니다.

변경 후 커밋하려면

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

2

Flask Shell에서 SQL 쿼리를 실행하는 방법에 대한 간단한 답변입니다.

먼저 모듈을 매핑하십시오 (모듈 / 앱이 주 폴더에 있고 manage.py이고 UNIX 운영 체제에있는 경우).

export FLASK_APP=manage

플라스크 쉘 실행

flask shell

필요한 것을 가져 오십시오 ::

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

쿼리를 실행하십시오.

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

응용 프로그램이있는 현재 데이터베이스 연결을 사용합니다.


0

문서에connection.execute(text( <sql here> ), <bind params here> ) 설명 대로 매개 변수를 사용 하고 바인딩 했습니까 ? 이를 통해 많은 매개 변수 형식 및 성능 문제를 해결할 수 있습니다. 아마도 게이트웨이 오류가 시간 초과입니까? 바인드 매개 변수는 복잡한 쿼리를 훨씬 빠르게 실행하는 경향이 있습니다.


2
문서 에 따르면 이어야합니다 connection.execute(text(<sql here>), <bind params> ). bind params에 있지 않아야합니다 text(). 실행] () 메소드에 바인딩 파라미터 먹이
제이크 버거

제이크의 연결이 끊어졌습니다. 나는 이것이 지금 관련된 URL이라고 생각한다 : docs.sqlalchemy.org/en/latest/core/…
code_dredd

-1

당신이 튜플을 피하려면 다른 방법은 호출하는 것입니다 first, one또는 all방법 :

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

assert query.first().name == "Welcome to my blog"
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.