I'm very new to SQLAlchemy and I'm trying to figure it out.
Please have in mind the following test setup:
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)
That translates into the following DDLs:
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);
All tables are empty. Then, the following code statements:
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()
Can somebody please explain why is the above code snippet executing without errors? Should't the FK constraints stop from inserting a new row into seven
or three
? I assume there is something wrong with how the FKs are described in the classes themselves, but I don't know where the problem is (and how to fix it).
[Edit 1]
Adding __table_args__
for all classes (forgot to include them).
[Edit 2]
Adding DDLs for further reference.
Foreign keys in SQL are used to create a link between two tables. It is a column that creates a relationship between the tables by taking a reference from another table in the same database using the primary key. The foreign key may contain null and duplicate values.
How to Add a Foreign Key to an Existing Table. You can not use the ALTER TABLE statement to add a foreign key in SQLite. Instead you will need to rename the table, create a new table with the foreign key, and then copy the data into the new table.
A foreign key in SQL is a table-level construct that constrains one or more columns in that table to only allow values that are present in a different set of columns, typically but not always located on a different table.
SQLite has supported foreign key constraint since version 3.6. 19. The SQLite library must also be compiled with neither SQLITE_OMIT_FOREIGN_KEY nor SQLITE_OMIT_TRIGGER. To check whether your current version of SQLite supports foreign key constraints or not, you use the following command.
SQLite by default does not enforce ForeignKey constraints (see here http://www.sqlite.org/pragma.html#pragma_foreign_keys )
To enable, follow these docs here: http://docs.sqlalchemy.org/en/latest/dialects/sqlite.html#foreign-key-support
Here's a copy paste of the official documentation:
SQLite supports FOREIGN KEY syntax when emitting CREATE statements for tables, however by default these constraints have no effect on the operation of the table.
Constraint checking on SQLite has three prerequisites:
- At least version 3.6.19 of SQLite must be in use
- The SQLite library must be compiled without the SQLITE_OMIT_FOREIGN_KEY or SQLITE_OMIT_TRIGGER symbols enabled.
- The
PRAGMA foreign_keys = ON
statement must be emitted on all connections before use. SQLAlchemy allows for the PRAGMA statement to be emitted automatically for new connections through the usage of events:
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()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With