SQAlchemy 自定义二级关系与复合主键

Ale*_*aru 5 sqlalchemy

有 3 个表:AccountRoleUser。双方RoleUser有一个外键account_id,它指向Account

用户可以具有多个角色,因此roles_users充当之间的二次关系表的表RoleUser

Account表是我们应用程序的租户表,用于分隔不同的客户。

请注意,所有表(除此之外Account)都具有与account_id. 这样做有几个原因,但我们假设这样做是为了保持一切一致。

现在,如果我有一个简单的次要关系(User.roles- 被注释掉的那个),一切都按预期工作。好吧..它抛出一个合法的警告(尽管我认为它应该是一个错误):

SAWarning: relationship 'User.roles' will copy column role.account_id to column roles_users.account_id, which conflicts with relationship(s): 'User.roles' (copies user.account_id to roles_users.account_id). Consider applying viewonly=True to read-only relationships, or provide a primaryjoin condition marking writable columns with the foreign() annotation.
Run Code Online (Sandbox Code Playgroud)

这就是为什么我创建了第二个关系User.roles——那个没有被注释掉的关系。查询按预期工作,其中有 2 个条件加入和一切。但是,当我尝试在用户上保存一些角色时出现此错误:

sqlalchemy.orm.exc.UnmappedColumnError: Can't execute sync rule for source column 'roles_users.role_id'; mapper 'Mapper|User|user' does not map this column.  Try using an explicit `foreign_keys` collection which does not include destination column 'role.id' (or use a viewonly=True relation).
Run Code Online (Sandbox Code Playgroud)

据我了解,SA 无法弄清楚如何保存辅助对象,因为它有一个自定义primaryjoinsecondaryjoin因此它建议使用viewonly=Truewhich 具有在保存模型时忽略角色关系的效果。

问题是如何为用户保存角色而不必手动完成(示例在代码中注释掉了)。在真实的应用程序中,我们有许多次要关系,我们将它们保存在许多地方。将它们全部重写将是非常困难的。

是否有解决方案可以User.roles = some_roles在保留自定义primaryjoinsecondaryjoin以下内容的同时继续使用?

使用 SA 1.1.9 的完整示例:

SAWarning: relationship 'User.roles' will copy column role.account_id to column roles_users.account_id, which conflicts with relationship(s): 'User.roles' (copies user.account_id to roles_users.account_id). Consider applying viewonly=True to read-only relationships, or provide a primaryjoin condition marking writable columns with the foreign() annotation.
Run Code Online (Sandbox Code Playgroud)

注意:切换primaryjoinsecondaryjoin没有帮助。

Ale*_*aru 0

为了后代的解决方案 - 切换外部包装器并小心主连接和辅助连接:

而不是这个:

    roles = relationship(
        Role,
        secondary=roles_users,
        primaryjoin=and_(foreign(Role.id) == roles_users.c.role_id,
                         Role.account_id == roles_users.c.account_id),
        secondaryjoin=and_(foreign(id) == roles_users.c.user_id,
                           account_id == roles_users.c.account_id))
Run Code Online (Sandbox Code Playgroud)

做这个:


    roles = relationship(
        Role,
        secondary=roles_users,
        primaryjoin=and_(id == foreign(roles_users.c.user_id), account_id == foreign(roles_users.c.account_id)),
        secondaryjoin=and_(Role.id == foreign(roles_users.c.role_id), Role.account_id == roles_users.c.account_id),
    )
Run Code Online (Sandbox Code Playgroud)