2013-08-01 47 views
0

我有一個包含點和對象概念的數據庫。對象與一對一關係中的單個點相關聯。一次只能有一個對象與一個點關聯,這是我通過唯一性約束強制執行的。允許引用不存在的記錄的SQLAlchemey中的複合外鍵

但是,我總是想說,對象只能與存在的點關聯,但我很難看到如何實現這一點。

在我進一步解釋,這裏是一些測試代碼,我有:

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複合主鍵,我想對Obj使用xy作爲外鍵。

backref工程和唯一性約束意味着沒有2個OBJ文件可以佔用相同的觀點,但我的測試代碼允許我創建於不存在Point(-1,-1)時,我想這是一個Obj預防。

這是可能實現的,我該怎麼做呢?

回答

1

如果你真的在使用SQLite,不僅僅是爲了演示的目的,問題是它目前不默認強制外鍵約束(在3.6.19之前並沒有強制執行它們)。在開始任何交易之前,您必須執行PRAGMA foreign_keys=ON

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中這可以使用事件系統實現

sqlalchemy.exc.IntegrityError: (IntegrityError) 
foreign key constraint failed u'INSERT INTO obj (x, y) VALUES (?, ?)' (-1, -1) 

注意,這個錯誤是在沖洗只養/提交時間:它是由數據庫引擎報告的,SQLAlchemy無法知道您創建了一個無效對象:它不會跟蹤Python端的FK。

+0

對它進行排序,完美!是的,我目前正在開發SQLite,只是爲了方便起見,雖然我開始懷疑如果這樣的「陷阱」是否很方便 – chrisbunney