I have many-to-many relationships for users and roles and I want to select user which have specific roles using realtions. For example I want to get user having:
roles = ["role_1", "role_2", "role_3"]
so I tried
query.filter(Users.roles.contains(roles))
(where roles – List[Roles])
but I got
sqlalchemy.exc.ArgumentError: Mapped instance expected for relationship comparison to object. Classes, queries and other SQL elements are not accepted in this context; for comparison with a subquery, use Users.roles.has(**criteria).
then I tried
query.filter(Users.roles.has(Roles.name.in_(roles)))
where roles already List[str]
And I got
sqlalchemy.exc.InvalidRequestError: 'has()' not implemented for collections. Use any().
but any() selects entry that has any associated role when I need entry that has all required roles. So how to select it right way using relationships instead of joins and etc.?
class Users(sa.Model):
__tablename__ = 'users'
id = Column(Integer, primary_key=True, autoincrement=True)
login = Column(String(50), unique=False)
roles = relationship('Roles', secondary='user_roles_map',
cascade='all, delete')
class Roles(sa.Model):
__tablename__ = 'roles'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(40), unique=True)
class UserRolesMap(sa.Model):
__tablename__ = 'user_roles_map'
id_seq = Sequence(__tablename__ + "_id_seq")
id = Column(Integer(), id_seq, server_default=id_seq.next_value(),
unique=True, nullable=False)
user_id = Column(
Integer, ForeignKey('users.id'),
primary_key=True)
role_id = Column(
Integer, ForeignKey('roles.id'),
primary_key=True)
Advertisement
Answer
I didn’t find what I was looking for, so for now I just wrote it with joins:
query = db_session.query(Users)
.filter_by(**parameters)
.join(UserRolesMap, UserRolesMap.user_id == Users.id)
.filter(UserRolesMap.role_id.in_(roles_ids))
.group_by(Users)
.having(func.count(UserRolesMap.role_id) >= len(roles_ids))
where roles_ids was collected from Roles table before. And if you need user with only required roles you can replace “>=” with “==”.