2010-04-10 19 views
24

La nuova versione di SQLite ha la capacità di applicare i vincoli di chiave esterna, ma per motivi di compatibilità con le versioni precedenti, è necessario attivarla separatamente per ciascuna connessione di database!Sqlite/SQLAlchemy: come imporre le chiavi esterne?

sqlite> PRAGMA foreign_keys = ON; 

Sto usando SQLAlchemy - come posso assicurarmi che questo venga sempre attivato? Quello che ho cercato è questo:

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

... ma non funziona ... Che cosa mi manca?

EDIT: Penso che il mio vero problema è che ho più di una versione di SQLite installata, e Python non utilizza la più recente!

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

Ma ho appena scaricato 3.6.23 e mettere l'exe nella mia directory di progetto! Come posso capire quale .exe sta usando e cambiarlo?

risposta

14

ora ho questo lavoro:

scaricare l'ultima build SQLite e pysqlite2 come sopra descritto: assicurarsi che le versioni corrette vengono utilizzati in fase di runtime pitone.

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

Successivamente aggiungere un 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()]) 

Poi fare attenzione a come si prova se le chiavi esterne stanno lavorando: ho avuto una certa confusione qui. Quando si utilizza sqlalchemy ORM per aggiungere(), il mio codice di importazione gestiva implicitamente gli hookup delle relazioni, quindi non avrebbe mai potuto fallire. L'aggiunta di 'nullable = False' ad alcune istruzioni di ForeignKey() mi ha aiutato qui.

Il modo in cui ho test sqlalchemy SQLite supporto chiave esterna è abilitato è quello di fare un inserto manuale da una classe dichiarativa 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) 

Qui 'wall_id' e 'type_id' sono entrambi ForeignKey() 's e sqlite genera un'eccezione correttamente ora se prova ad agganciare fkeys non validi. Quindi funziona! Se rimuovi il listener, sqlalchemy aggiungerà felicemente voci non valide.

Credo che il problema principale potrebbe essere rappresentato da più sqlite3.dll (o .so) in giro.

+0

Hai usato il PRAGMA nello stesso modo in cui l'ho fatto? –

+0

Grazie, ho funzionato anche io. In effetti, il problema era costituito da più copie di SQLite sulla mia macchina ... risolvendolo, e l'utilizzo di PoolListener ha funzionato perfettamente! –

+0

Funziona! Ma come farlo funzionare con poolevent ... – 42n4

3

Ho avuto lo stesso problema in precedenza (gli script con chiavi esterne stavano attraversando ma i vincoli attuall non erano stati applicati dal motore sqlite); ottenuto è stato inviato da:

  1. download, costruire e installare l'ultima versione di SQLite da qui: sqlite-sqlite-amalgamation; prima di questo avevo sqlite 3.6.16 sulla mia macchina Ubuntu; che non supportava ancora le chiavi esterne; dovrebbe essere 3.6.19 o superiore per farli funzionare.

  2. installare l'ultima versione di pysqlite da qui: pysqlite-2.6.0

dopo che ho cominciato ad avere eccezioni ogni volta vincolo di chiave esterna non è riuscita

speranza che questo aiuta, saluti

+0

Ho già SQLite 3.6.23 e pysqlite 2.6.0 (e nuovo SQLAlchemy) Il documento SQLite dice che è necessario attivare esplicitamente l'applicazione FK. Nella tua esperienza, quando ha fatto rispettare, hai usato quella cosa PRAGMA? –

+0

sì, ho "PRAGMA foreign_keys = ON;" nel mio codice –

2

Se è necessario eseguire qualcosa per l'installazione su ogni connessione, utilizzare un PoolListener.

+0

Grazie - Ho provato PoolListener e mi ha permesso di eseguire il pragma per ogni connessione al database! Perfezionare! ... tranne che il pragma non funziona ancora! Il motore SQLite non applica ancora le chiavi esterne! ... Mi manca ancora un pezzo del puzzle. Forse è perché sono su Windows? I documenti SQLite dicono qualcosa sulle "opzioni di compilazione" con cui è stato costruito ... ma ho appena ottenuto l'installazione standard per Windows ... non sono sicuro se questo è importante? –

37

Per le versioni più recenti (SQLAlchemy ~ 0,7) il SQLAlchemy homepage dice:

PoolListener è deprecato. Si prega di fare riferimento a PoolEvents.

Poi l'esempio Carls diventa:

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 risposta di conny è perfetta per le versioni più recenti di sqlalchemy. Usalo! Il moderatore dovrebbe davvero scegliere questo come corretto. –

3

Come un approccio più semplice se la creazione della sessione è centralizzata dietro una funzione Python helper (anziché esporre direttamente al motore SQLA), si può solo problema " session.execute ('pragma foreign_keys = on') "prima di ritornare alla sessione appena creata.

È necessario l'approccio del listener di pool solo se parti arbitrarie dell'applicazione possono creare sessioni SQLA sul database.

+0

Più semplice e buona risposta! Questo funziona benissimo per me –

6

Dal SQLite dialect page:

SQLite supporta la sintassi FOREIGN KEY quando emettono le istruzioni CREATE per le tabelle, invece di default questi vincoli non hanno alcun effetto sul funzionamento del tavolo.

vincolo controllando SQLite ha tre condizioni:

  • Almeno versione 3.6.19 di SQLite deve essere in uso
  • Il libary SQLite deve essere compilato senza la SQLITE_OMIT_FOREIGN_KEY o SQLITE_OMIT_TRIGGER simboli abilitati.
  • L'istruzione PRAGMA foreign_keys = ON deve essere emessa su tutte le connessioni prima dell'uso.

SQLAlchemy consente per l'istruzione PRAGMA per essere emessa automaticamente per le nuove connessioni attraverso l'utilizzo di eventi:

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

Basandosi sulle risposte da Conny e shadowmatter, ecco il codice che verificherà se stanno usando SQLite3 prima di emettere l'istruzione 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

Grazie. Questo funziona per quelli di noi che preferiscono l'approccio 'db = SQLAlchemy (app)'. –

Problemi correlati