python - 后期绑定多对多自引用关系的语法
问题描述
我发现了很多关于如何使用单独的表或类创建自我引用的多对多关系(对于用户关注者或朋友)的解释:
以下是三个示例,其中一个来自 Mike Bayer 本人:
- sqlalchemy中的多对多自引用关系
- 如何在 SQLAlchemy ORM 上实现对同一属性的自引用多对多关系?
- Miguel Grinberg 的 Flask Megatutorial 关于追随者
但是在我发现的每个示例中,在关系中定义primaryjoin
and的语法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
所有对象的列时,如Mixins: Augmenting the base from the docs
我的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
但是现在在我将's 移到 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_flrs.follower_id = :follower_id_1'
关系上的主连接条件的本地映射外键列User.followed_users
。确保引用列与ForeignKey
或相关联ForeignKeyConstraint
,或者在连接条件中使用注释进行foreign()
注释。要允许比较运算符以外'=='
的,可以将关系标记为viewonly=True
。
如果我将括号更改为引号以利用后期绑定,它会引发相同的错误。我不知道如何用 and 注释这个东西,foreign()
因为remote()
我根本不知道 sqlalchemy 希望我在跨辅助表的自引用关系上将什么描述为外部和远程!我已经尝试了很多这种组合,但到目前为止还没有奏效。
我有一个非常相似(尽管不相同)的问题,即不跨越单独表的自引用关系,关键是将remote_side
参数转换为后期绑定的。这对我来说很有意义,因为id
在早期绑定过程中该列不存在。
如果我遇到问题的不是后期绑定,请告知。但是,在当前范围内,我的理解是id
映射到 Python 内置id()
,因此不能作为早期绑定关系。
在连接中转换id
为Base.id
会导致以下错误:
ArgumentError: 找不到任何简单的等式表达式,涉及
'user_flrs.follower_id = "<name unknown>"'
关系上的主连接条件的本地映射外键列User.followed_users
。确保引用列与ForeignKey
或相关联ForeignKeyConstraint
,或者在连接条件中使用注释进行foreign()
注释。要允许比较运算符以外'=='
的,可以将关系标记为viewonly=True
。
解决方案
您不能id
在联接过滤器中使用,不,因为那是内置id()
函数,而不是User.id
列。
你有三个选择:
创建模型后定义关系
User
,将其分配给新User
属性;然后,您可以参考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' )
对于后两个选项,请参阅sqlalchemy.orgm.relationship()
文档:
可选地接受的一些参数
relationship()
接受一个可调用函数,该函数在调用时会产生所需的值。父映射器在“映射器初始化”时调用可调用对象,这仅在第一次使用映射器时发生,并且假定在所有映射都已构建之后。这可用于解决声明顺序和其他依赖问题,例如 ifChild
在下面Parent
的同一文件中声明*[.]*[...]
使用声明式扩展时,声明式初始化程序允许将字符串参数传递给
relationship()
. 这些字符串参数被转换为可调用函数,将字符串评估为 Python 代码,使用声明性类注册表作为命名空间。这允许通过其字符串名称自动查找相关类,并且完全无需将相关类导入本地模块空间*[.]*[...]
主要加入-
[...]
primaryjoin
也可以作为可调用函数传递,该函数在映射器初始化时进行评估,并且可以在使用声明时作为 Python 可评估字符串传递。[...]
辅助连接–</p>
[...]
secondaryjoin
也可以作为可调用函数传递,该函数在映射器初始化时进行评估,并且可以在使用声明时作为 Python 可评估字符串传递。
字符串和 lambda 都定义了与第一个选项中使用的user_flrs.c.followed_id == User.id
/user_flrs.c.follower_id == User.id
表达式相同的表达式,但是因为它们分别作为字符串和可调用函数给出,所以您推迟评估直到 SQLAlchemy 需要完成这些声明。
推荐阅读
- apache-nifi - 用于将数据从 RDMBS 导入 HDFS 的 Apache NiFi - 与 SQOOP 的性能比较
- android - camera.setPreviewDisplay(holder); 录制视频时通过空指针异常
- jmeter - 在 JMeter 生成的 Report Dashboard 中包含自定义表格
- typescript - 多类型变量的通用约束
- c# - 检查是否设置了图像或在 xamarin.forms 中有图片
- python-3.6 - 如何从熊猫数据框中删除基线?
- c# - Hibernate 是否将“0”值更改为“NULL”?
- spring - 我使用 resttemplate 来测试我的 Web 服务,但是当我运行测试时,我与应用程序的连接出现错误
- c++ - 为什么这会更新到矢量中的地图
- php - 如何使用路由过滤器检查 Laravel 5.8 中登录用户的角色