SQLalchemy和模棱两可的ForeignKeys

时间:2017-08-19 18:28:32

标签: python sqlalchemy

更新:完整的工作代码示例为here。)

Many-To-One关系

class City(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32), index=True)
    country_id = db.Column(db.Integer, db.ForeignKey('country.id'))
    country = db.relationship('Country', back_populates='cities')

class Country(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32), index=True)
    cities = db.relationship('City', back_populates='country')

可以augmented区分众多之一,比如资本。

在这两个解决方案中,添加单独的表是可行的

class Capital(db.Model):
    # https://stackoverflow.com/q/45767923/8099646
    id = db.Column(db.Integer, primary_key=True)
    country = db.Column(db.Integer, db.ForeignKey('country.id'))
    capital = db.Column(db.Integer, db.ForeignKey('city.id'))

但更好的解决方案是避免额外的表格,只需插入一个字段:

class Country(db.Model):
    ...
    capital = db.Column(db.Integer, db.ForeignKey('city.id'))

为什么SQLalchemy会在添加该行时抱怨AmbiguousForeignKeysError

1 个答案:

答案 0 :(得分:1)

原因是现在SQLAlchemy不知道country / cities关系所引用的连接条件,因为有两种可能性(即country_idcapital )。

解决此问题的方法是明确指定foreign_keys

class City(db.Model):
    ...
    country = db.relationship('Country', foreign_keys=country_id, back_populates='cities')

class Country(db.Model):
    ...
    cities = db.relationship('City', foreign_keys=City.country_id, back_populates='country')

请注意,这种循环模式在创建表时也会导致CircularDependencyError(因为每个都不能在没有另一个的情况下创建)以及插入时具有两个相互依赖的关系(因为每个行都不能插入而没有另一个行的ID)。因此,完整的解决方案是在其中一个use_alter上设置ForeignKey(在创建表时发出ALTER以便打破周期)并在其中一个上设置post_update relationship s(在插入时发出UPDATE以打破周期):

class City(db.Model):
    ...
    country_id = db.Column(db.Integer, db.ForeignKey('country.id'))
    country = db.relationship('Country', foreign_keys=country_id, back_populates='cities')

class Country(db.Model):
    ...
    capital_id = db.Column(db.Integer, db.ForeignKey('city.id', use_alter=True))
    capital = db.relationship(City, foreign_keys=capital_id, post_update=True)
    cities = db.relationship(City, foreign_keys=City.country_id, back_populates='country')