Python 延迟绑定多对多自引用关系的语法
对于如何使用单独的表或类创建自引用多对多关系(针对用户追随者或朋友),我找到了许多解释: 下面是三个例子,其中一个来自迈克·拜耳本人:Python 延迟绑定多对多自引用关系的语法,python,sqlalchemy,many-to-many,self-reference,Python,Sqlalchemy,Many To Many,Self Reference,对于如何使用单独的表或类创建自引用多对多关系(针对用户追随者或朋友),我找到了许多解释: 下面是三个例子,其中一个来自迈克·拜耳本人: 但在我发现的每个示例中,在关系中定义primaryjoin和secondaryjoin的语法都是早期绑定语法: # this relationship is used for persistence friends = relationship("User", secondary=friendship,
primaryjoin
和secondaryjoin
的语法都是早期绑定语法:
# this relationship is used for persistence
friends = relationship("User", secondary=friendship,
primaryjoin=id==friendship.c.friend_a_id,
secondaryjoin=id==friendship.c.friend_b_id,
)
这非常有效,除了一种情况:使用Base
类为所有对象定义id
列,如文档中的所示
我的Base
类和followers
表定义如下:
from flask_sqlchalchemy import SQLAlchemy
db = SQLAlchemy()
class Base(db.Model):
__abstract__ = True
id = db.Column(db.Integer, primary_key=True)
user_flrs = db.Table(
'user_flrs',
db.Column('follower_id', db.Integer, db.ForeignKey('user.id')),
db.Column('followed_id', db.Integer, db.ForeignKey('user.id')))
但是现在,在我将id
移动到mixin之前,我的追随者关系已经忠诚地为我服务了一段时间,我遇到了麻烦:
class User(Base):
__table_name__ = 'user'
followed_users = db.relationship(
'User', secondary=user_flrs, primaryjoin=(user_flrs.c.follower_id==id),
secondaryjoin=(user_flrs.c.followed_id==id),
backref=db.backref('followers', lazy='dynamic'), lazy='dynamic')
db.class_mapper(User) # trigger class mapper configuration
大概是因为id
不在本地作用域中,尽管它似乎抛出了一个奇怪的错误:
ArgumentError:找不到任何涉及主联接条件的本地映射外键列的简单等式表达式'user\u flrs.follower\u id=:follower\u id\u 1'
关于关系user。跟随的\u users
。确保引用列与ForeignKey
或ForeignKeyConstraint
关联,或在连接条件中使用foreign()
注释进行注释。要允许使用除'=='
以外的比较运算符,可以将关系标记为viewonly=True
如果我将括号更改为引号以利用后期绑定,它会抛出相同的错误。我不知道如何用foreign()
和remote()
来注释这件事,因为我根本不知道sqlalchemy希望我在一个跨二级表的自引用关系上描述什么是foreign和remote!我已经尝试了很多组合,但到目前为止还没有成功
我有一个非常类似的问题(虽然不完全相同),即没有跨单独表的自引用关系,关键是将远程
参数转换为后期绑定的参数。这对我来说很有意义,因为id
列在早期绑定过程中不存在
如果我遇到的问题不是延迟装订,请告知。但是,在当前范围内,我的理解是id
映射到Python内置的id()
,因此不能作为早期绑定关系使用
将联接中的id
转换为Base.id
,会导致以下错误:
ArgumentError:在关系用户上找不到任何涉及主联接条件'user\u flrs.follower\u id=“”
的本地映射外键列的简单等式表达式。跟随的\u用户
。确保引用列与ForeignKey
或ForeignKeyConstraint
关联,或在连接条件中使用foreign()
注释进行注释。要允许使用除'=='
以外的比较运算符,可以将关系标记为viewonly=True
不能在加入筛选器中使用
id
,因为这是,而不是User.id
列
您有三种选择:
用户
模型后定义关系,将其分配给新的用户
属性;然后,您可以引用User.id
,因为它已从基础中拉入:
class User(Base):
# ...
User.followed_users = db.relationship(
User,
secondary=user_flrs,
primaryjoin=user_flrs.c.follower_id == User.id,
secondaryjoin=user_flrs.c.followed_id == User.id,
backref=db.backref('followers', lazy='dynamic'),
lazy='dynamic'
)
relationship()
的任何字符串参数都将作为Python表达式计算,而不仅仅是第一个参数:
class User(Base):
# ...
followed_users = db.relationship(
'User',
secondary=user_flrs,
primaryjoin="user_flrs.c.follower_id == User.id",
secondaryjoin="user_flrs.c.followed_id == User.id",
backref=db.backref('followers', lazy='dynamic'),
lazy='dynamic'
)
class User(Base):
# ...
followed_users = db.relationship(
'User',
secondary=user_flrs,
primaryjoin=lambda: user_flrs.c.follower_id == User.id,
secondaryjoin=lambda: user_flrs.c.followed_id == User.id,
backref=db.backref('followers', lazy='dynamic'),
lazy='dynamic'
)
relationship()
接受的某些参数可以选择接受可调用函数,调用该函数时会生成所需的值。父映射器在“映射器初始化”时调用该可调用函数,这仅在第一次使用映射器时发生,并假定在构造所有映射之后发生。这可用于解决声明顺序和其他依赖性问题,例如,如果在同一文件中的父项下声明了子项
*
[……]
使用声明性扩展时,声明性初始值设定项允许将字符串参数传递给relationship()
。这些字符串参数被转换为可调用函数,这些可调用函数使用声明性类注册表作为命名空间,以Python代码的形式对字符串进行求值。这允许通过相关类的字符串名称自动查找相关类,并且完全不需要将相关类导入本地模块空间*[。]*
[……]
- primaryjoin——
[……]
primaryjoin
还可以作为可调用函数传递,该函数在映射器初始化时进行计算,并且在使用声明性语句时可以作为Python可计算字符串传递
[……]
- 第二次加入——
[……]
secondaryjoin
也可以作为可调用函数传递,该函数在映射器初始化时进行计算,并且在使用声明性语句时可以作为Python可计算字符串传递
字符串和lambda都定义了与第一个选项中使用的相同的user\u flrs.c.followered\u id==user.id
/user\u flrs.c.follower\u id==user.id
表达式,但由于它们分别作为字符串和可调用函数提供,因此您将计算推迟到SQLAlchemy需要这些表达式时进行