web-dev-qa-db-ja.com

Sqlite / SQLAlchemy:外部キーを適用する方法は?

新しいバージョンのSQLiteには外部キー制約を適用する機能がありますが、下位互換性のために、データベース接続ごとに個別にオンにする必要があります。

sqlite> PRAGMA foreign_keys = ON;

SQLAlchemyを使用しています-これが常にオンになっていることを確認するにはどうすればよいですか?私が試したことはこれです:

engine = sqlalchemy.create_engine('sqlite:///:memory:', echo=True)
engine.execute('pragma foreign_keys=on')

...しかしそれは機能していません!...私は何が欠けていますか?

EDIT:私の本当の問題は、複数のバージョンのSQLiteがインストールされていて、Pythonが最新のものを使用していないことだと思います!

>>> import sqlite3
>>> print sqlite3.sqlite_version
3.3.4

しかし、3.6.23をダウンロードして、exeをプロジェクトディレクトリに配置しました。使用している.exeを特定し、変更するにはどうすればよいですか?

33
Nick Perkins

私は今これを働いています:

上記のように最新のsqliteおよびpysqlite2ビルドをダウンロードします。実行時にPythonが正しいバージョンを使用していることを確認してください。

_import sqlite3   
import pysqlite2 
print sqlite3.sqlite_version   # should be 3.6.23.1
print pysqlite2.__path__       # eg C:\\Python26\\lib\\site-packages\\pysqlite2
_

次に、PoolListenerを追加します。

_from sqlalchemy.interfaces import PoolListener
class ForeignKeysListener(PoolListener):
    def connect(self, dbapi_con, con_record):
        db_cursor = dbapi_con.execute('pragma foreign_keys=ON')

engine = create_engine(database_url, listeners=[ForeignKeysListener()])
_

次に、外部キーが機能しているかどうかをテストする方法に注意してください。ここで混乱が生じました。 sqlalchemy ORMを使用してadd()する場合、インポートコードは暗黙的にリレーションのフックアップを処理していたため、失敗することはありませんでした。いくつかのForeignKey()ステートメントに_nullable=False_を追加すると、ここで役に立ちました。

Sqlalchemy sqlite外部キーサポートが有効になっていることをテストする方法は、宣言型ORMクラスから手動で挿入することです。

_# example
ins = Coverage.__table__.insert().values(id = 99,
                                    description = 'Wrong',
                                    area = 42.0,
                                    wall_id = 99,  # invalid fkey id
                                    type_id = 99)  # invalid fkey_id
session.execute(ins) 
_

ここで、_wall_id_と_type_id_はどちらもForeignKey()であり、無効なfkeyをフックしようとすると、sqliteは例外を正しくスローします。だからそれはうまくいきます!リスナーを削除すると、sqlalchemyは無効なエントリを喜んで追加します。

主な問題は、複数のsqlite3.dll(または.so)が周りにある可能性があると思います。

15
CarlS

最近のバージョン(SQLAlchemy〜0.7)の場合 SQLAlchemyホームページ は次のように述べています。

PoolListenerは非推奨です。 PoolEvents を参照してください。

次に、CarlSによる例は次のようになります。

engine = create_engine(database_url)

def _fk_pragma_on_connect(dbapi_con, con_record):
    dbapi_con.execute('pragma foreign_keys=ON')

from sqlalchemy import event
event.listen(engine, 'connect', _fk_pragma_on_connect)
42
conny

Connyとshadowmatterからの回答に基づいて、PRAGMAステートメントを発行する前にSQLite3を使用しているかどうかを確認するコードを次に示します。

from sqlalchemy import event
from sqlalchemy.engine import Engine
from sqlite3 import Connection as SQLite3Connection

@event.listens_for(Engine, "connect")
def _set_sqlite_pragma(dbapi_connection, connection_record):
    if isinstance(dbapi_connection, SQLite3Connection):
        cursor = dbapi_connection.cursor()
        cursor.execute("PRAGMA foreign_keys=ON;")
        cursor.close()
30

SQLite方言ページ から:

SQLiteは、テーブルのCREATEステートメントを発行するときにFOREIGN KEY構文をサポートしますが、デフォルトでは、これらの制約はテーブルの操作に影響を与えません。

SQLiteの制約チェックには、次の3つの前提条件があります。

  • 少なくともバージョン3.6.19のSQLiteが使用されている必要があります
  • SQLiteライブラリは、SQLITE_OMIT_FOREIGN_KEYまたはSQLITE_OMIT_TRIGGERシンボルを有効にせずにコンパイルする必要があります。
  • PRAGMA external_keys = ONステートメントは、使用する前にすべての接続で発行する必要があります。

SQLAlchemyを使用すると、イベントを使用して、新しい接続に対してPRAGMAステートメントを自動的に発行できます。

from sqlalchemy.engine import Engine
from sqlalchemy import event

@event.listens_for(Engine, "connect")
def set_sqlite_pragma(dbapi_connection, connection_record):
    cursor = dbapi_connection.cursor()
    cursor.execute("PRAGMA foreign_keys=ON")
    cursor.close()
10
shadowmatter

セッションの作成が(SQLAエンジンを直接公開するのではなく)Pythonヘルパー関数の背後に集中している場合のより簡単なアプローチとして、新しく作成したものを返す前にsession.execute('pragma foreign_keys=on')を発行できます。セッション。

アプリケーションの任意の部分がデータベースに対してSQLAセッションを作成する可能性がある場合にのみ、プールリスナーアプローチが必要です。

5
ncoghlan

私は以前に同じ問題を抱えていました(外部キー制約のあるスクリプトは通過していましたが、実際の制約はsqliteエンジンによって強制されませんでした)。それを解決した:

  1. ここから最新バージョンのsqliteをダウンロード、ビルド、インストールします: sqlite-sqlite-amalgamation ;この前は、ubuntuマシンにsqlite3.6.16がありました。これはまだ外部キーをサポートしていませんでした。それらを機能させるには、3.6.19以上である必要があります。

  2. ここから最新バージョンのpysqliteをインストールします: pysqlite-2.6.

その後、外部キー制約が失敗するたびに例外が発生し始めました

これがお役に立てば幸いです

3
serge_gubenko

すべての接続でセットアップのために何かを実行する必要がある場合は、 PoolListener を使用します。

2
Ants Aasma