后期绑定多对多自引用关系的语法
Syntax for late-binding many-to-many self-referential relationship
我找到了很多关于如何使用单独的 table 或 class 创建自我引用的多对多关系(对于用户关注者或朋友)的解释:
以下是三个示例,其中一个来自 Mike Bayer 本人:
- Many-to-many self-referential relationship in sqlalchemy
- How can I achieve a self-referencing many-to-many relationship on the SQLAlchemy ORM back referencing to the same attribute?
- Miguel Grinberg's Flask Megatutorial on followers
但在我发现的每个示例中,定义关系中 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
class 为所有对象定义 id
列时,如 Mixins: Augmenting the base 所示来自文档
我的Base
class和followers
table是这样定义的:
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: Could not locate any simple equality expressions involving locally mapped foreign key columns for primary join condition 'user_flrs.follower_id = :follower_id_1'
on relationship User.followed_users
. Ensure that referencing columns are associated with a ForeignKey
or ForeignKeyConstraint
, or are annotated in the join condition with the foreign()
annotation. To allow comparison operators other than '=='
, the relationship can be marked as viewonly=True
.
如果我将括号更改为引号以利用后期绑定,它会抛出相同的错误。我不知道如何用 foreign()
和 remote()
注释这个东西,因为我根本不知道 sqlalchemy 希望我在跨越次要 table!我已经尝试了很多这样的组合,但到目前为止还没有奏效。
我遇到了一个非常相似(虽然不完全相同)的问题,自引用关系不跨越一个单独的table,关键是简单地转换后期绑定的 remote_side
参数。这对我来说很有意义,因为 id
列在早期绑定过程中不存在。
如果我遇到的问题不是后期绑定,请指教。不过,在当前范围内,我的理解是 id
映射到 Python 内置 id()
,因此不会作为早期绑定关系工作。
在联接中将 id
转换为 Base.id
会导致以下错误:
ArgumentError: Could not locate any simple equality expressions involving locally mapped foreign key columns for primary join condition 'user_flrs.follower_id = "<name unknown>"'
on relationship User.followed_users
. Ensure that referencing columns are associated with a ForeignKey
or ForeignKeyConstraint
, or are annotated in the join condition with the foreign()
annotation. To allow comparison operators other than '=='
, the relationship can be marked as viewonly=True
.
您不能在联接过滤器中使用 id
,不,因为那是 built-in id()
function,而不是 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()
documentation:
Some arguments accepted by relationship()
optionally accept a callable function, which when called produces the desired value. The callable is invoked by the parent Mapper at “mapper initialization” time, which happens only when mappers are first used, and is assumed to be after all mappings have been constructed. This can be used to resolve order-of-declaration and other dependency issues, such as if Child
is declared below Parent
in the same file*[.]*
[...]
When using the Declarative extension, the Declarative initializer allows string arguments to be passed to relationship()
. These string arguments are converted into callables that evaluate the string as Python code, using the Declarative class-registry as a namespace. This allows the lookup of related classes to be automatic via their string name, and removes the need to import related classes at all into the local module space*[.]*
[...]
primaryjoin –
[...]
primaryjoin
may also be passed as a callable function which is evaluated at mapper initialization time, and may be passed as a Python-evaluable string when using Declarative.
[...]
secondaryjoin –
[...]
secondaryjoin
may also be passed as a callable function which is evaluated at mapper initialization time, and may be passed as a Python-evaluable string when using Declarative.
字符串和 lambda 都定义了与第一个选项中使用的相同的 user_flrs.c.followed_id == User.id
/ user_flrs.c.follower_id == User.id
表达式,但是因为它们分别作为字符串和可调用函数给出,所以您推迟计算直到 SQLAlchemy 需要完成这些声明。
我找到了很多关于如何使用单独的 table 或 class 创建自我引用的多对多关系(对于用户关注者或朋友)的解释:
以下是三个示例,其中一个来自 Mike Bayer 本人:
- Many-to-many self-referential relationship in sqlalchemy
- How can I achieve a self-referencing many-to-many relationship on the SQLAlchemy ORM back referencing to the same attribute?
- Miguel Grinberg's Flask Megatutorial on followers
但在我发现的每个示例中,定义关系中 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
class 为所有对象定义 id
列时,如 Mixins: Augmenting the base 所示来自文档
我的Base
class和followers
table是这样定义的:
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: Could not locate any simple equality expressions involving locally mapped foreign key columns for primary join condition
'user_flrs.follower_id = :follower_id_1'
on relationshipUser.followed_users
. Ensure that referencing columns are associated with aForeignKey
orForeignKeyConstraint
, or are annotated in the join condition with theforeign()
annotation. To allow comparison operators other than'=='
, the relationship can be marked asviewonly=True
.
如果我将括号更改为引号以利用后期绑定,它会抛出相同的错误。我不知道如何用 foreign()
和 remote()
注释这个东西,因为我根本不知道 sqlalchemy 希望我在跨越次要 table!我已经尝试了很多这样的组合,但到目前为止还没有奏效。
我遇到了一个非常相似(虽然不完全相同)的问题,自引用关系不跨越一个单独的table,关键是简单地转换后期绑定的 remote_side
参数。这对我来说很有意义,因为 id
列在早期绑定过程中不存在。
如果我遇到的问题不是后期绑定,请指教。不过,在当前范围内,我的理解是 id
映射到 Python 内置 id()
,因此不会作为早期绑定关系工作。
在联接中将 id
转换为 Base.id
会导致以下错误:
ArgumentError: Could not locate any simple equality expressions involving locally mapped foreign key columns for primary join condition
'user_flrs.follower_id = "<name unknown>"'
on relationshipUser.followed_users
. Ensure that referencing columns are associated with aForeignKey
orForeignKeyConstraint
, or are annotated in the join condition with theforeign()
annotation. To allow comparison operators other than'=='
, the relationship can be marked asviewonly=True
.
您不能在联接过滤器中使用 id
,不,因为那是 built-in id()
function,而不是 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()
documentation:
Some arguments accepted by
relationship()
optionally accept a callable function, which when called produces the desired value. The callable is invoked by the parent Mapper at “mapper initialization” time, which happens only when mappers are first used, and is assumed to be after all mappings have been constructed. This can be used to resolve order-of-declaration and other dependency issues, such as ifChild
is declared belowParent
in the same file*[.]*[...]
When using the Declarative extension, the Declarative initializer allows string arguments to be passed to
relationship()
. These string arguments are converted into callables that evaluate the string as Python code, using the Declarative class-registry as a namespace. This allows the lookup of related classes to be automatic via their string name, and removes the need to import related classes at all into the local module space*[.]*[...]
primaryjoin –
[...]
primaryjoin
may also be passed as a callable function which is evaluated at mapper initialization time, and may be passed as a Python-evaluable string when using Declarative.[...]
secondaryjoin –
[...]
secondaryjoin
may also be passed as a callable function which is evaluated at mapper initialization time, and may be passed as a Python-evaluable string when using Declarative.
字符串和 lambda 都定义了与第一个选项中使用的相同的 user_flrs.c.followed_id == User.id
/ user_flrs.c.follower_id == User.id
表达式,但是因为它们分别作为字符串和可调用函数给出,所以您推迟计算直到 SQLAlchemy 需要完成这些声明。