5

Sono molto nuovo a SQLAlchemy e sto cercando di capirlo.Come aggiungere correttamente i vincoli di chiave esterna a SQLite DB utilizzando SQLAlchemy

Si prega di avere in mente la seguente configurazione di prova:

class Nine(Base): 
    __tablename__ = 'nine' 
    __table_args__ = (sqlalchemy.sql.schema.UniqueConstraint('nine_b', name='uq_nine_b'),) 

    nine_a = sqlalchemy.Column(sqlalchemy.dialects.sqlite.INTEGER(), primary_key=True, autoincrement=False, nullable=False) 
    nine_b = sqlalchemy.Column(sqlalchemy.String(20), nullable=False) 


class Seven(Base): 
    __tablename__ = 'seven' 
    __table_args__ = (sqlalchemy.sql.schema.PrimaryKeyConstraint('seven_a', 'seven_b'), 
         sqlalchemy.sql.schema.Index('fk_seven_c_nine_a_idx', 'seven_c'),) 

    seven_a = sqlalchemy.Column(sqlalchemy.dialects.sqlite.INTEGER(), nullable=False) 
    seven_b = sqlalchemy.Column(sqlalchemy.dialects.sqlite.INTEGER(), nullable=False) 
    seven_c = sqlalchemy.Column(sqlalchemy.dialects.sqlite.INTEGER(), sqlalchemy.ForeignKey('nine.nine_a'), nullable=False) 
    seven_d = sqlalchemy.Column(sqlalchemy.dialects.sqlite.INTEGER(), nullable=False) 

    nine = sqlalchemy.orm.relationship(Nine, backref=sqlalchemy.orm.backref('seven'), uselist=False) 


class Three(Base): 
    __tablename__ = 'three' 
    __table_args__ = (sqlalchemy.sql.schema.UniqueConstraint('three_b', 'three_c', name='uq_three_b_c'), 
         sqlalchemy.sql.schema.Index('fk_three_c_seven_a_idx', 'three_c'),) 

    three_a = sqlalchemy.Column(sqlalchemy.dialects.sqlite.INTEGER(), primary_key=True, autoincrement=True, nullable=False) 
    three_b = sqlalchemy.Column(sqlalchemy.dialects.sqlite.INTEGER(), nullable=False) 
    three_c = sqlalchemy.Column(sqlalchemy.dialects.sqlite.INTEGER(), sqlalchemy.ForeignKey('seven.seven_a'), nullable=False) 

    seven = sqlalchemy.orm.relationship(Seven, backref=sqlalchemy.orm.backref('three'), uselist=False) 

che si traduce nei seguenti DDL:

CREATE TABLE nine (
    nine_a INTEGER NOT NULL, 
    nine_b VARCHAR(20) NOT NULL, 
    PRIMARY KEY (nine_a), 
    CONSTRAINT uq_nine_b UNIQUE (nine_b) 
); 

CREATE TABLE seven (
    seven_a INTEGER NOT NULL, 
    seven_b INTEGER NOT NULL, 
    seven_c INTEGER NOT NULL, 
    seven_d INTEGER NOT NULL, 
    PRIMARY KEY (seven_a, seven_b), 
    FOREIGN KEY(seven_c) REFERENCES nine (nine_a) 
); 

CREATE INDEX fk_seven_c_nine_a_idx ON seven (seven_c); 

CREATE TABLE three (
    three_a INTEGER NOT NULL, 
    three_b INTEGER NOT NULL, 
    three_c INTEGER NOT NULL, 
    PRIMARY KEY (three_a), 
    CONSTRAINT uq_three_b_c UNIQUE (three_b, three_c), 
    FOREIGN KEY(three_c) REFERENCES seven (seven_a) 
); 

CREATE INDEX fk_three_c_seven_a_idx ON three (three_c); 

Tutti i tavoli sono vuoti. Quindi, le seguenti dichiarazioni di codice:

session.add(Nine(nine_a=1, nine_b='something')) 
session.add(Nine(nine_a=2, nine_b='something else')) 
session.commit() 

session.add(Seven(seven_a=7, seven_b=7, seven_c=7, seven_d=7)) 
session.commit() 

session.add(Three(three_a=3, three_b=3, three_c=3)) 
sessionDB.commit() 

Qualcuno può spiegare perché il frammento di codice sopra riportato viene eseguito senza errori? I vincoli FK non dovrebbero interrompere l'inserimento di una nuova riga in seven o three? Presumo che ci sia qualcosa di sbagliato nel modo in cui gli FK sono descritti nelle classi stesse, ma non so dove sia il problema (e come risolverlo).

[Modifica 1]

Aggiunta __table_args__ per tutte le classi (dimenticato di includerli).

[Modifica 2]

Aggiunta DDL per ulteriore riferimento.

risposta

3

SQLite per default non rispettare i vincoli ForeignKey (vedi qui http://www.sqlite.org/pragma.html#pragma_foreign_keys)

Per attivare, attenersi alla seguente documentazione qui: http://docs.sqlalchemy.org/en/latest/dialects/sqlite.html#foreign-key-support

Ecco una pasta copia della documentazione ufficiale:

SQLite supporta ESTERO Sintassi KEY quando si emettono le istruzioni CREATE per le tabelle, tuttavia per impostazione predefinita questi vincoli non hanno alcun effetto sul funzionamento della tabella.

controllo dei vincoli su SQLite ha tre presupposti:

almeno la versione 3.6.19 di SQLite deve essere in uso La libreria SQLite deve essere compilato senza le 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() 
+0

sto emissione 'PRAGMA FOREIGN_KEYS = ON' prima di eseguire gli inserti (il comando' PRAGMA foreign_keys' restituisce '1 'Purtroppo gli stessi risultati – Yeseanul

+0

Ho testato il vincolo FK su ON a livello di DB, ma sembra che questo debba essere attivato per ogni connessione ([http://stackoverflow.com/a/9835781/1237714] (http://stackoverflow.com/a/9835781/1237714)). Le cose stanno iniziando a diventare ** normali **. (Si prega di includere snippet di codice (ad esempio, utilizzando eventi) in risposta.) – Yeseanul

+0

Mentre questo può teoricamente rispondere alle domanda, [sarebbe preferibile] (// meta.stackoverflow.com/q/8259) per includere le parti essenziali della risposta a lei e, e fornire il link per riferimento. – SuperBiasedMan

Problemi correlati