【问题标题】:Relationship to "jump" through M2M relationships in SqlAlchemy (User -> Role -> Permission)通过 SqlAlchemy 中的 M2M 关系“跳转”的关系(用户 -> 角色 -> 权限)
【发布时间】:2019-09-23 16:22:54
【问题描述】:

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

如果我能从User 直接获得relationshipPermission,那将非常有帮助。这样,我可以从数据库中获取一个用户(实例),然后执行user.permissions 来获取他的权限,进行一些过滤以检查用户是否有特定的Permission,让他们预先-loaded... 总之:relationship 附带的所有好东西。

viewonly 关系会非常好。正如 Mike Bayer 在a very similar question 中提到的,我不能写信给User.permissions,因为我们不知道要使用哪个“角色”或在哪里插入它。

我创建了两个中间表:

User -- M2M --> Role(s) -- M2M --> Permission(s)
 |                                     ^
 +-------- user.permissions -----------+
  • users_roles 通过主键(ID)将user 连接到其roles
  • roles_permissions将每个role连接到它的permissions

这是我的表结构(为问题而简化,但即使是完整版也非常典型而且......简单)

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.

任何提示将不胜感激。提前谢谢你。

【问题讨论】:

    标签: python sqlalchemy relationship m2m


    【解决方案1】:

    二级不应该包括相关的表本身,而只是它们之间的关联:

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

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

    【讨论】:

    • 天啊...小时!!...我花了几个小时尝试事物的不同组合!非常感谢。
    • 这里的概念是join有效地创建了一个单一的关联表吗?
    • @SuperShoot 差不多。您可以使用任何表(存在外键)作为辅助表,无论是连接、选择还是诸如此类的结果:docs.sqlalchemy.org/en/13/orm/…。由于 SQLA 处理去重实体的方式,即使连接多次产生相同的权限 ID,也不是问题。
    • 即使 join 多次产生相同的权限 ID,这也不是问题。 我可以对此进行评估。我创建了一个测试,其中 user 属于两个不同的角色,每个角色都具有相同的权限,并且在我的 user.permissions 关系中,即使我没有指定 collection_class = setrelationship。这让我有点担心(获得重复的权限)但没有:它不会发生......它工作得很好:+1:
    猜你喜欢
    • 2022-01-12
    • 2017-03-17
    • 2011-02-05
    • 2016-05-07
    • 1970-01-01
    • 1970-01-01
    • 2016-10-18
    • 2017-03-18
    • 1970-01-01
    相关资源
    最近更新 更多