通过 SqlAlchemy 中的 M2M 关系与 "jump" 的关系(用户 -> 角色 -> 权限)

Relationship to "jump" through M2M relationships in SqlAlchemy (User -> Role -> Permission)

我正在尝试建立一个非常典型的权限结构模型,其中可以将 User 模型(我网站的人类用户)分配给某些 Roles,并且每个 Roles 有一些 Permissions.

如果我能从 User 直接得到 relationshipPermission,那将非常有帮助。这样,我可以从数据库中获取一个 user(实例),然后执行 user.permissions 以获得他的权限,进行一些过滤以检查用户是否具有特定的 Permission,让它们预加载...总而言之:relationship.

附带的所有好东西

viewonly 关系就很好了。正如 Mike Bayer 在 a very similar question 中提到的,我不能写信给 User.permissions 因为我们不知道要使用哪个 'Role' 或 哪里 插入它。

我创建了两个中间 tables:

User -- M2M --> Role(s) -- M2M --> Permission(s)
 |                                     ^
 +-------- user.permissions -----------+

这是我的table结构(针对问题进行了简化,但即使是完整版也非常典型且...简单)

class User(DeclarativeBase, Mixin):
    __tablename__ = 'users'
    email = Column(String(25), nullable=False)

_users_roles = Table('users_roles', DeclarativeBase.metadata,
    Column('user_id', ForeignKey('users.id', ...
    Column('role_id', ForeignKey('roles.id', ...
    PrimaryKeyConstraint('user_id', 'role_id',),
)    

class Role(DeclarativeBase, Mixin):
    __tablename__ = 'roles'

    name = Column(Text(), nullable=False, unique=True)
    users = relationship("User", secondary=_users_roles, backref="roles")

_roles_permissions = Table('roles_permissions', DeclarativeBase.metadata,
    Column('role_id', ForeignKey('roles.id',  ...
    Column('permission_id', ForeignKey('permissions.id', ...
    PrimaryKeyConstraint('role_id', 'permission_id',),
)

class Permission(DeclarativeBase, Mixin):
    __tablename__ = 'permissions'

    key = Column(Text, nullable=False, unique=True,) 

我看到 this other answer 看起来很有前途,但我似乎无法让它发挥作用。老实说,我一直在尝试很多组合,我会说我得到的最远的是:

permissions = relationship('Permission',
    secondary="""join(User, users_roles, 
                     User.id == users_roles.c.user_id
                 ).join(roles_permissions, 
                    users_roles.c.role_id == roles_permissions.c.role_id
                ).join(
                  Permission, roles_permissions.c.permission_id == Permission.id
               )""",
    viewonly=True,
)

这给了我这个错误:

sqlalchemy.exc.ArgumentError: Relationship User.permissions 
could not determine any unambiguous local/remote column pairs 
based on join condition and remote_side arguments.  Consider 
using the remote() annotation to accurately mark those elements
of the join condition that are on the remote side of the relationship.

如有任何提示,我们将不胜感激。提前谢谢你。

辅助不应包括相关表本身,而应包括它们之间的关联:

permissions = relationship(
    'Permission',
    secondary="""join(users_roles, roles_permissions, 
                      users_roles.c.role_id == roles_permissions.c.role_id)""",
    viewonly=True,
)

在辅助表中加入要连接的表会混淆试图通过辅助查找 UserPermission 之间的外键关系的自动化。