Flask を使用して、SQLAlchemy がSELECT x FROM table WHERE ...
ORM を使用する代わりに未加工の SQL (直接) でデータベースにクエリを実行するための最良の方法であるかどうか、またはよりシンプルで強力な代替手段があるかどうかを知りたいです。
お返事ありがとうございます。
Flask を使用して、SQLAlchemy がSELECT x FROM table WHERE ...
ORM を使用する代わりに未加工の SQL (直接) でデータベースにクエリを実行するための最良の方法であるかどうか、またはよりシンプルで強力な代替手段があるかどうかを知りたいです。
お返事ありがとうございます。
私は常に SQLAlchemy を直接クエリに使用しています。
主な利点: SQL インジェクション攻撃に対する最高の保護を提供します。SQLAlchemy は、どんなパラメーターを投げても正しいことを行います。
条件に基づいて生成された SQL を調整する場合にも、驚くほど機能することがわかりました。その上に複数のフィルター コントロールがある結果セットを表示しますか? if/elif/else コンストラクトのセットでクエリを作成するだけで、SQL が依然としてゴールデンであることがわかります。
以下は、いくつかのライブ コードからの抜粋です (古い SA バージョンのため、構文が少し異なる可能性があります)。
# Pull start and end dates from form
# ...
# Build a constraint if `start` and / or `end` have been set.
created = None
if start and end:
created = sa.sql.between(msg.c.create_time_stamp,
start.replace(hour=0, minute=0, second=0),
end.replace(hour=23, minute=59, second=59))
elif start:
created = (msg.c.create_time_stamp >=
start.replace(hour=0, minute=0, second=0))
elif end:
created = (msg.c.create_time_stamp <=
end.replace(hour=23, minute=59, second=59))
# More complex `from_` object built here, elided for example
# [...]
# Final query build
query = sa.select([unit.c.eli_uid], from_obj=[from_])
query = query.column(count(msg.c.id).label('sent'))
query = query.where(current_store)
if created:
query = query.where(created)
これが元になるコードはもっと複雑ですが、ここで日付範囲コードを強調したいと思います。文字列の書式設定を使用して SQL を作成する必要がある場合、値を引用するのを忘れやすいため、おそらくどこかに SQL インジェクション ホールを導入していたでしょう。
私の小さなプロジェクトに取り組んだ後、SQL Alchemy を使用せずに MySQLDB だけを使用することにしました。
これは問題なく動作し、非常に使いやすいです。例を次に示します(データベースへのすべての作業を処理する小さなクラスを作成しました)
import MySQLdb
from MySQLdb.cursors import DictCursor
class DatabaseBridge():
def __init__(self, *args, **kwargs):
kwargs['cursorclass'] = DictCursor
self.cnx = MySQLdb.connect (**kwargs)
self.cnx.autocommit(True)
self.cursor = self.cnx.cursor()
def query_all(self, query, *args):
self.cursor.execute(query, *args)
return self.cursor.fetchall()
def find_unique(self, query, *args):
rows = self.query_all(query, *args);
if len(rows) == 1:
return rows[0]
return None
def execute(self, query, params):
self.cursor.execute(query, params)
return self.cursor.rowcount
def get_last_id(self):
return self.cnx.insert_id()
def close(self):
self.cursor.close()
self.cnx.close()
database = DatabaseBridge(**{
'user': 'user',
'passwd': 'password',
'db': 'my_db'
})
rows = database.query_all("SELECT id, name, email FROM users WHERE is_active = %s AND project = %s", (1, "My First Project"))
(これはばかげた例です)。
それは魅力のように機能しますが、これらを考慮する必要があります:
multiprocessing
Python から操作しなくても大丈夫です。しかし一方で、SQLAlchemy と同様に、SQL インジェクション攻撃に対する保護があります。
基本的なクエリは次のようになります。
cursor.execute("SELECT * FROM users WHERE data = %s" % "Some value") # THIS IS DANGEROUS
しかし、次のことを行う必要があります。
cursor.execute("SELECT * FROM users WHERE data = %s", ("Some value",)) # This is secure!
違いを見ましたか?もう一度読んでください;)
%
違いは、 , を:に置き換えたこと,
です。引数を ... 引数として実行に渡し、これらはエスケープされます。を使用する%
と、引数がエスケープされず、SQL インジェクション攻撃が可能になります!
ここでの最後の言葉は、それはあなたの使用法とあなたのプロジェクトで何をするつもりかによるということです. 私にとって、SQLAlchemy はやり過ぎだったので (これは基本的なシェル スクリプトです!)、MysqlDB は完璧でした。