Sqlite / SQLAlchemy:如何强制执行外键?

时间:2010-04-10 21:07:16

标签: python sqlite foreign-keys 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并进行更改?

8 个答案:

答案 0 :(得分:40)

对于最新版本(SQLAlchemy~0.7),SQLAlchemy homepage说:

  

不推荐使用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)

答案 1 :(得分:27)

基于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()

答案 2 :(得分:15)

我现在有这个工作:

如上所述下载最新的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添加()时,我的导入代码隐式处理关系连接,因此永远不会失败。在某些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()和sqlite,如果尝试连接无效的fkeys,则会正确抛出异常。所以它有效!如果你删除了监听器,那么sqlalchemy会很乐意添加无效的条目。

我认为主要问题可能是多个sqlite3.dll(或.so)。

答案 3 :(得分:9)

来自SQLite dialect page

  

SQLite在为表发出CREATE语句时支持FOREIGN KEY语法,但默认情况下,这些约束对表的操作没有影响。

     

SQLite上的约束检查有三个先决条件:

     
      
  • 至少版本3.6.19的SQLite必须正在使用
  •   
  • 必须在未启用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()

答案 4 :(得分:5)

作为一种更简单的方法,如果您的会话创建集中在Python辅助函数后面(而不是直接暴露SQLA引擎),您可以在返回刚创建的之前发出“session.execute('pragma foreign_keys = on')”会话。

如果应用程序的任意部分可能会针对数据库创建SQLA会话,则只需要池侦听器方法。

答案 5 :(得分:3)

之前我遇到了同样的问题(带有外键约束的脚本正在通过,但是sqlite引擎没有强制执行约束);得到它解决:

  1. 从这里下载,构建和安装最新版本的sqlite:sqlite-sqlite-amalgamation;在此之前,我的ubuntu机器上有sqlite 3.6.16;它还不支持外键;让它们工作应该是3.6.19或更高。

  2. 从这里安装最新版本的pysqlite:pysqlite-2.6.0

  3. 之后,每当外键约束失败时我就开始获得异常

    希望这有帮助,尊重

答案 6 :(得分:2)

如果您需要在每个连接上执行某些设置,请使用PoolListener

答案 7 :(得分:1)

单行版本的conny's answer

from sqlalchemy import event
event.listen(engine, 'connect', lambda c, _: c.execute('pragma foreign_keys=on'))