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需要这些表达式时进行