2010-04-10 18 views
24

La nueva versión de SQLite tiene la capacidad de hacer cumplir las restricciones de clave externa, pero en aras de la compatibilidad con versiones anteriores, hay que activarlo para cada conexión de base de datos por separado!Sqlite/SQLAlchemy: ¿cómo hacer cumplir las claves externas?

sqlite> PRAGMA foreign_keys = ON; 

Estoy usando SQLAlchemy - ¿cómo puedo asegurarme de que siempre esté activado? Lo que he intentado es la siguiente:

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

... pero no funciona ... ¿Qué me falta?

EDIT: Creo que mi problema real es que tengo más de una versión de SQLite instalado, y Python no está utilizando la última uno!

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

¡Pero acabo de descargar 3.6.23 y puse el exe en el directorio de mi proyecto! ¿Cómo puedo averiguar qué .exe que está usando, y cambiarlo?

Respuesta

14

ahora tengo este trabajo:

descargar la última SQLite y pySqlite2 aumenta a medida que se ha descrito anteriormente: Asegúrese de que las versiones correctas se están utilizando en tiempo de ejecución 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 

A continuación añadimos una 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()]) 

entonces tenga cuidado de cómo se prueba si las claves externas están trabajando: Tenía cierta confusión aquí. Al usar sqlalchemy ORM para agregar() elementos, mi código de importación manejaba implícitamente las conexiones de relación, por lo que nunca podría fallar. Agregar 'nullable = False' a algunas declaraciones ForeignKey() me ayudó aquí.

A mi modo de prueba el apoyo clave externa sqlite sqlalchemy se habilita es hacer una inserción manual desde una clase declarativa 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) 

Aquí 'wall_id' y 'TYPE_ID' son ambos ForeignKey() 's y sqlite arroja una excepción correctamente ahora si intenta conectar nocivos inválidos. ¡Así que funciona! Si elimina el oyente, sqlalchemy agregará entradas inválidas.

Creo que el problema principal puede ser de múltiples sqlite3.dll (o .so) por ahí.

+0

¿Usó el PRAGMA de la misma manera que lo he hecho? –

+0

Gracias, lo tengo trabajando también. De hecho, el problema consistía en varias copias de SQLite en mi máquina ... ¡arreglar eso y usar PoolListener han funcionado perfectamente! –

+0

¡Funciona! Pero cómo hacer que funcione con poolevent ... – 42n4

3

que tenían el mismo problema antes (scripts con las limitaciones de claves externas estaban pasando pero actuallos limitaciones no fueron aplicadas por el motor SQLite); lo consiguió resuelto por:

  1. descarga, construcción e instalación de la última versión de SQLite desde aquí: sqlite-sqlite-amalgamation; antes de esto tenía sqlite 3.6.16 en mi máquina ubuntu; que aún no soportaba claves extranjeras; debería ser 3.6.19 o superior para que funcionen.

  2. instalar la última versión de pysqlite desde aquí: pysqlite-2.6.0

después de eso empecé a recibir excepciones cuando restricción de clave externa no

esperanza que esta ayuda, que se refiere a

+0

Ya tengo SQLite 3.6.23 y pysqlite 2.6.0 (y nueva SQLAlchemy) El documento de SQLite dice que debes activar explícitamente la aplicación de FK. En su experiencia, cuando hizo cumplir, ¿usó esa cosa PRAGMA? –

+0

sí, tengo "PRAGMA foreign_keys = ON;" en mi código –

2

Si necesita ejecutar algo para la configuración en cada conexión, use un PoolListener.

+0

Gracias - Probé PoolListener, ¡y me permitió ejecutar el pragma para cada conexión de base de datos! ¡Perfecto! ... ¡excepto que el pragma todavía no funciona! ¡El motor SQLite todavía no aplica llaves foráneas! ... Todavía me falta una pieza del rompecabezas. Tal vez es porque estoy en Windows? Los documentos SQLite dicen algo sobre las "opciones de compilación" con las que fue construido ... pero acabo de obtener la instalación estándar para Windows ... ¿no estoy seguro de que eso importe? –

37

Para las versiones recientes (~ 0,7) SQLAlchemy la SQLAlchemy homepage dice:

PoolListener está en desuso. Por favor, consulte PoolEvents.

Entonces el ejemplo CarlS se convierte en:

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) 
+0

la respuesta de conny es perfecta para las versiones más nuevas de sqlalchemy. Úselo! El moderador realmente debería elegir este como correcto. –

3

Como un enfoque más simple si su creación de la sesión se centraliza detrás de una función auxiliar Python (en lugar de exponer el motor SQLA directamente), puede simplemente cuestión " session.execute ('pragma foreign_keys = on') "antes de devolver la sesión recién creada.

Solo necesita el enfoque de escucha de grupo si partes arbitrarias de su aplicación pueden crear sesiones SQLA en la base de datos.

+0

¡Respuesta más simple y buena! Este es un gran trabajo para mí –

6

Desde el SQLite dialect page:

SQLite apoya sintaxis de FOREIGN KEY al emitir sentencias CREATE para las tablas, sin embargo, de forma predeterminada estas limitaciones no tienen ningún efecto sobre el funcionamiento de la mesa.

la comprobación de restricciones en SQLite tiene tres requisitos previos:

  • al menos la versión 3.6.19 de SQLite debe estar en uso
  • El libary SQLite debe ser compilado sin los SQLITE_OMIT_FOREIGN_KEY o SQLITE_OMIT_TRIGGER símbolos habilitados.
  • La instrucción PRAGMA foreign_keys = ON debe emitirse en todas las conexiones antes de su uso.

SQLAlchemy permite la declaración PRAGMA que se emitirá automáticamente las nuevas conexiones a través del uso de eventos:

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() 
19

Sobre la base de las respuestas de Conny y shadowmatter, aquí está el código que comprobará si están usando SQLite3 antes de emitir la instrucción PRAGMA:

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() 
+0

Gracias. Esto funciona para aquellos de nosotros que también preferimos el enfoque 'db = SQLAlchemy (app)'. –

Cuestiones relacionadas