如何正确定义 SQLAlchemy backrefs 以便它们可以反映?
Posted
技术标签:
【中文标题】如何正确定义 SQLAlchemy backrefs 以便它们可以反映?【英文标题】:How to properly define SQLAlchemy backrefs so that they can be reflected? 【发布时间】:2018-10-22 16:49:19 【问题描述】:假设我们在某个 Models.py 文件中有以下代码:
class Person(db.Model):
__tablename__ = 'Persons'
ID = db.Column(db.Integer, primary_key=True, nullable=False)
Name = db.Column(db.String(255), nullable=False)
class House(db.Model):
__tablename__ = 'Houses'
ID = db.Column(db.Integer,primary_key=True,nullable=False)
OwnerID = db.Column(db.Integer, nullable=False)
TenantID = db.Column(db.Integer, nullable=False)
__table_args__ = (
db.ForeignKeyConstraint(
['OwnerID'],
['Persons.ID'],
),
db.ForeignKeyConstraint(
['TenantID'],
['Persons.ID'],
),
)
OwnerBackref = db.relationship('Person', backref='OwnerBackref', lazy=True, foreign_keys=[OwnerID])
TenantBackref = db.relationship('Person', backref='TenantBackref', lazy=True, foreign_keys=[TenantID])
并且我们希望使用自动映射库来反映这些模型,因此我们将这段代码放在另一个名为 Database.py 的模块中:
Base = automap_base()
engine = create_engine(DB_CONNECTION, pool_size=10, max_overflow=20)
db_session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))
Base.prepare(engine, reflect=True)
Person = Base.classes.Persons
House = Base.classes.Houses
现在,当我在其他模块中导入 House 时,我希望能够这样做:
h = db_session.query(House).first()
print(h.OwnerBackref.Name)
print(h.TenantBackref.Name)
但是我收到一个错误,说这 2 个反向引用不存在,而是将一个名为“persons”的字段添加到我的 House 对象中,但这里的问题是它仅链接 1 个(租户或所有者)。我的意思是,如果我这样做:
print(h.persons.Name)
它只会为相应的租户或所有者打印名称,让我无法访问另一个租户的信息。 (请注意,我为 backrefs 设置的名称无处可寻)
那么,我的问题是如何使用我创建的反向引用来访问我想要的信息?我在这里做错了吗?
【问题讨论】:
【参考方案1】:您的代码中的错误是您使用foreign_keys=
来定义表之间的关系,但是您将本地键名而不是外键名传递给函数。对于您的代码,您不能使用 foreign_keys=
来定义 House 模型中的关系,因为只有一个可能的外键 Person.ID
但可能有两个可能的本地键 House.OwnerID
和 House.TenantID
。应该使用 primaryjoin=
参数来指定这一点。
class Person(db.Model):
__tablename__ = 'Persons'
ID = db.Column(db.Integer, primary_key=True)
Name = db.Column(db.String(255), nullable=False)
class House(db.Model):
__tablename__ = 'Houses'
ID = db.Column(db.Integer,primary_key=True)
OwnerID = db.Column(db.Integer, db.ForeignKey('Persons.ID'), nullable=False)
TenantID = db.Column(db.Integer, db.ForeignKey('Persons.ID'), nullable=False)
Owner = db.relationship('Person', backref='HousesOwned', primaryjoin='House.OwnerID == Person.ID')
Tenant = db.relationship('Person', backref='HousesOccupied', primaryjoin='House.TenantID == Person.ID')
如果您将关系语句放在 Person 模型而不是 House 模型中,那么您可以使用 foreign_keys=
或 primaryjoin=
来定义关系。下面的代码将产生与前面代码完全相同的关系。
class Person(db.Model):
__tablename__ = 'Persons'
ID = db.Column(db.Integer, primary_key=True)
Name = db.Column(db.String(255), nullable=False)
HousesOwned = db.relationship('House', backref='Owner', foreign_keys='[House.OwnerID]')
HousesOccupied = db.relationship('House', backref='Tenant', foreign_keys='[House.TenantID]')
class House(db.Model):
__tablename__ = 'Houses'
ID = db.Column(db.Integer,primary_key=True)
OwnerID = db.Column(db.Integer, db.ForeignKey('Persons.ID'), nullable=False)
TenantID = db.Column(db.Integer, db.ForeignKey('Persons.ID'), nullable=False)
【讨论】:
我尝试了你们的建议,但不幸的是它们似乎对我不起作用。第一个的行为与我在问题中描述的相同:为我的 House 对象创建了一个名为“persons”的字段,所以如果我执行以下操作:h = models.db_session.query(models.House).first() print (h.persons.ID) 它只会打印租户的 ID 或所有者的 ID。对于第二个版本,我在我的 Peron 对象中得到一个“houses_collection”字段,但它给了我一个空列表: p = models.db_session.query(models.Person).first() print(p.houses.collection)以上是关于如何正确定义 SQLAlchemy backrefs 以便它们可以反映?的主要内容,如果未能解决你的问题,请参考以下文章
SQLAlchemy学习-5.relationship之backref和back_populates参数
从 SQLAlchemy 查询中检索不同的 backref 实例