Sqlite / SQLAlchemy:如何执行外键?
新版本的SQLite有强制外键约束的能力,但是为了向后兼容,你必须为每个数据库连接单独打开它!
sqlite> PRAGMA foreign_keys = ON;
我正在使用SQLAlchemy – 我怎样才能确保它始终打开? 我试过的是这样的:
engine = sqlalchemy.create_engine('sqlite:///:memory:', echo=True) engine.execute('pragma foreign_keys=on')
…但它不工作!…我错过了什么?
编辑:我认为我的真正的问题是,我有不止一个版本的SQLite安装,并没有使用最新的Python!
>>> import sqlite3 >>> print sqlite3.sqlite_version 3.3.4
但是我只是下载了3.6.23,并把exe文件放在我的项目目录下! 我怎样才能找出它正在使用的.exe,并改变它?
我现在有这个工作:
按照上面的描述下载最新的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()])
那么请小心如何testing外键是否正常工作:我在这里有一些困惑。 当使用sqlalchemy ORM添加()时,我的导入代码隐式地处理关系连接,因此永远不会失败。 在一些ForeignKey()语句中join'nullable = False'帮助我在这里。
我testingsqlalchemy 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()的,而sqlite现在会抛出一个exception,现在如果试图连接无效的fkeys。 所以它的作品! 如果你删除了监听器,那么sqlalchemy会愉快地添加无效的条目。
我相信主要的问题可能是多个sqlite3.dll的(或.so)左右。
对于最近的版本(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)
基于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()
从SQLite方言页面 :
当为表发出CREATE语句时,SQLite支持FOREIGN KEY语法,但默认情况下,这些约束对表的操作没有影响。
SQLite上的约束检查有三个先决条件:
- SQLite至less必须使用3.6.19版本
- 必须在不启用SQLITE_OMIT_FOREIGN_KEY或SQLITE_OMIT_TRIGGER符号的情况下编译SQLite库。
- PRAGMA foreign_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()
我以前有同样的问题(外键约束的脚本正在经历,但实际的约束不是由sqlite引擎强制执行); 解决了它:
-
从这里下载,build立和安装最新版本的sqlite: sqlite-sqlite-amalgamation ; 在这之前,我在我的ubuntu机器上安装了sqlite 3.6.16; 目前还不支持外键; 应该是3.6.19或更高,让他们工作。
-
从这里安装最新版本的pysqlite: pysqlite-2.6.0
之后,我开始收到exception,只要外键约束失败
希望这有助于问候
作为一个更简单的方法,如果你的会话创build集中在一个Python辅助函数的后面(而不是直接暴露SQLA引擎),你可以在返回新创build的会话之前发出“session.execute('pragma foreign_keys = on')”。
如果应用程序的任意部分可能会针对数据库创buildSQLA会话,则只需要池侦听器方法。
如果您需要在每个连接上执行设置,请使用PoolListener 。