Flask-SQLAlchemyアプリで生のSQLを実行する方法


219

SQLAlchemyで生のSQLをどのように実行しますか?

私はフラスコで実行され、SQLAlchemyを介してデータベースに接続するpython webアプリを持っています。

生のSQLを実行する方法が必要です。クエリには、インラインビューと共に複数のテーブル結合が含まれます。

私はもう試した:

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

しかし、ゲートウェイエラーが発生し続けます。


5
以前に見たことがありますが、アップデートの実行に関するチュートリアルは見つかりませんでした。また、構文を学習せずに、かなり長い(約20行)SQLクエリを隠します。
starwing123 2013

103
@MarkusUnterwaditzer以前はそう考えていましたが、今は強く反対します。生の適切にパラメーター化されたSQLは、一般に、それを生成する一連の関数呼び出しやオブジェクトよりも、読み取りと保守がはるかに簡単です。また、ORMに正しい構文を生成させるためにフープを飛ばす必要がなく(可能な場合でも)、データベースの全機能を提供し、ORMが予期しないことをしないようにします。「それではなぜSQLAlchemyを使用するのですか?」という質問をするかもしれませんが、私が持っている唯一の答えは、「既存のアプリケーションがSQLAlchemyを使用しており、すべてを変更するとコストがかかりすぎる」です。
jpmc26 2014

4
@ jpmc26コメントを更新しました。SQLの愛好者として、無責任な錬金術師に「データベースへのキーを提供する」という考えに苦労しており、ORMの側に寄りかかる傾向があります:)ユーザーの登録や管理などの特定のコンポーネントの高速化、およびアクション+ SQLをコーディングできる一連のボタンを備えたテーブルの生成にも力を注ぎたいと述べました。Pythonフレームワークでうまく機能するORM懐疑的なツールに出くわしましたか?
zx81

@ jpmc26 Pythonフレームワークでは、SQLだけを使用するために、またはC#Dapperのようにかなり近いものを使用しますか?Python Webフレームワークで目にするものはすべてSQLAlchemyを使用することを望んでおり、ORMは好きではありません。使用した場合、それは非常に最小限です。
ジョニー

@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
raw SQLを使用している場合はトランザクションを制御するため、BEGINand COMMITステートメントを自分で発行する必要があります。
ミゲル

1
SQLAlchemyなしで同じSQLコマンドを発行しても機能しますか?実行中のコマンドを確認できるように、データベースのデバッグを有効にすることができます。
ミゲル

27
db.engine.execute(text("<sql here>")).execution_options(autocommit=True))それも実行してコミットします。
Devi

8
@Miguel「生のSQLを使用している場合、トランザクションを制御するため、BEGINステートメントとCOMMITステートメントを自分で発行する必要があります。」これは単に真実ではありません。セッションオブジェクトで生のSQLを使用できます。このコメントに気づきましたが、生のSQLでセッションを使用する方法についての私の答えを見ることができます。
jpmc26 2015

180

SQL Alchemyセッションオブジェクトには独自のexecuteメソッドがあります。

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

すべてのアプリケーションクエリは、生のSQLかどうかにかかわらず、セッションオブジェクトを経由する必要があります。これにより、クエリがトランザクションによって適切に管理され、同じリクエスト内の複数のクエリを単一のユニットとしてコミットまたはロールバックできるようになります。エンジンまたは接続を使用してトランザクションの外に出ると、微妙なリスクが高まり、データが破損する可能性のあるバグを検出することが困難になる可能性があります。各リクエストは1つのトランザクションにのみ関連付ける必要がdb.sessionあり、これを使用することで、これがアプリケーションに当てはまります。

パラメータ化executeされたクエリ用に設計されていることにも注意してください:valSQLインジェクション攻撃から身を守るために、クエリへの入力には例のようにパラメータを使用してください。これらのパラメーターの値を指定するにdictは、2番目の引数としてa を渡します。各キーは、クエリに表示されるパラメーターの名前です。パラメータ自体の正確な構文はデータベースによって異なる場合がありますが、すべての主要なリレーショナルデータベースは何らかの形でそれらをサポートしています。

それはだと仮定すると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を返します。
アラン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/...
カール・

1
なぜ使用するの==か尋ねてもいいですか?
Nam G VU

1
@Jake Berger、ありがとうございました。私はこの答えを求めてほぼ一日を無駄にしました。私はテキストに変換せずに直接SQLを実行していました。where句に%students%が含まれていると、エラーが発生していました。あなたの答えに大きな拍手。
Suresh Kumar

1
@NamGVUは、ほとんどのプログラミング言語と同様に、=通常は値を割り当てるために予約されています。一方、 値==比較するために予約されています
Jake Berger

2
@JakeBergerそのためのリンクはありますか?SQLはそのような言語ではなく、SQLAlchemyドキュメントで判断するとそうではありません。
johndodo

36

SELECT SQLクエリの結果はfrom_statement()、とtext()を使用して取得できます。このようにタプルを扱う必要はありません。あなたが試すことができるUserテーブル名usersを持つクラスの例として、

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シェルを実行する

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()バインドパラメータをexecute()メソッドに供給する
Jake Berger

ジェイクのリンクが壊れています。私は、これは今関連性のURLだと思う:docs.sqlalchemy.org/en/latest/core/...
code_dredd

-1

タプルを避けたい場合は、別の方法としてfirstoneまたは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.