I am trying to model a very typical permission structure where a User
model (the human user of my website) can be assigned to some Roles
, and each of those Roles
has some Permissions
.
It would be really helpful if I could get a relationship
from User
straight to Permission
. That way, I could fetch a user (instance) from the database and just do user.permissions
to get his permissions, put some filtering to check if a user has a specific Permission
, have them pre-loaded... in sum: all the goodies that come with a relationship
.
A viewonly
relationship would be perfectly fine. As mentioned by Mike Bayer in a very similar question, I can't be writing to User.permissions
because we don't not know which 'Role' to use or where to insert it.
I have created two intermediate tables:
User -- M2M --> Role(s) -- M2M --> Permission(s)
| ^
+-------- user.permissions -----------+
users_roles
to connect theuser
to itsroles
through their primary key (the ID)- and
roles_permissions
to connect eachrole
to itspermissions
,
This is my table structure (simplified for the question, but even the full version is really typical and... simple)
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,)
I saw this other answer that looks very promising, yet I don't seem to be able to make it work. Honestly, I've been trying a lot of combinations and I'd say that the furthest I got was with this:
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,
)
Which gives me this error:
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.
Any hints would be greatly appreciated. Thank you in advance.