我有一个包含点和对象概念的数据库。对象与一对一关系中的单个点相关联。一次只能有一个对象与一个点关联,我已通过唯一性约束强制执行该操作。
但是,我总是想说对象只能与存在的点相关联,但我很难看到如何实现它。
在我进一步解释之前,这里有一些测试代码:
import sqlalchemy as sql
import sqlalchemy.orm as sqlorm
import sqlalchemy.ext.declarative
import os
debug = True
db_file = "/".join([os.getcwd() if __name__ == "__main__" else os.path.dirname(__file__), 'sqlite.db'])
engine_opts = {
'echo': debug
}
engine = sql.create_engine('sqlite:///{}'.format(db_file), **engine_opts)
Session = sqlorm.sessionmaker(bind=engine)
Base = sqlalchemy.ext.declarative.declarative_base()
class Point(Base):
__tablename__ = 'point'
x = sql.Column(sql.Integer, primary_key=True, nullable=False)
y = sql.Column(sql.Integer, primary_key=True, nullable=False)
def __repr__(self):
return self.__str__()
def __str__(self):
return "({x},{y})".format(x=self.x, y=self.y)
def __unicode__(self):
return self.__str__()
class Obj(Base):
__tablename__ = 'obj'
id = sql.Column(sql.Integer, primary_key=True)
x = sql.Column(sql.Integer)
y = sql.Column(sql.Integer)
__table_args__ = (
sql.ForeignKeyConstraint([x, y], [Point.x, Point.y]),
sql.UniqueConstraint(x, y, name='obj_coords'),
)
point = sqlorm.relationship("Point", backref=sqlorm.backref('obj', uselist=False))
def init_db():
Base.metadata.create_all(engine)
session = Session()
min_val = 0
max_val = 20
for x in range(min_val, max_val):
for y in range(min_val, max_val):
session.add(Point(x=x, y=y))
session.commit()
session.add(Obj(x=0, y=0))
session.add(Obj(x=10, y= 10))
session.add(Obj(x=-1, y=-1))
session.commit()
session.close()
def reinit_db():
Base.metadata.drop_all(engine)
init_db()
if __name__ == "__main__":
reinit_db()
我有Point
的复合主键,我想在x
上使用y
和Obj
作为外键。
backref
有效且唯一性约束意味着没有2个obj可以占据相同的点,但我的测试代码允许我在不存在的Obj
中创建Point
( - 1,-1)当我想要阻止它时。
这有可能实现,我该怎么做?
答案 0 :(得分:1)
如果您确实在使用SQLite,不仅仅是出于演示目的,问题在于它默认情况下它当前不强制执行外键约束(并且在3.6.19之前根本没有强制执行它们)。在开始任何交易之前,您必须执行PRAGMA foreign_keys=ON
。在SQLAlchemy中,这可以使用事件系统实现:
import sqlalchemy.event as sqlevent
# ...
engine = sql.create_engine('sqlite:///{0}'.format(db_file), **engine_opts)
sqlevent.listen(engine, 'connect',
lambda conn, rec: conn.execute('PRAGMA foreign_keys=ON;'))
然后执行程序会根据需要产生完整性错误:
sqlalchemy.exc.IntegrityError: (IntegrityError)
foreign key constraint failed u'INSERT INTO obj (x, y) VALUES (?, ?)' (-1, -1)
请注意,此错误仅在刷新/提交时引发:它由数据库引擎报告,并且SQLAlchemy无法知道您创建了无效对象:它不会在Python端跟踪FK。 / p>