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:
JavaScript
x
2
1
roles = ["role_1", "role_2", "role_3"]
2
so I tried
JavaScript
1
2
1
query.filter(Users.roles.contains(roles))
2
(where roles – List[Roles])
but I got
JavaScript
1
2
1
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).
2
then I tried
JavaScript
1
2
1
query.filter(Users.roles.has(Roles.name.in_(roles)))
2
where roles already List[str]
And I got
JavaScript
1
2
1
sqlalchemy.exc.InvalidRequestError: 'has()' not implemented for collections. Use any().
2
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.?
JavaScript
1
31
31
1
class Users(sa.Model):
2
__tablename__ = 'users'
3
4
id = Column(Integer, primary_key=True, autoincrement=True)
5
login = Column(String(50), unique=False)
6
7
roles = relationship('Roles', secondary='user_roles_map',
8
cascade='all, delete')
9
10
11
class Roles(sa.Model):
12
__tablename__ = 'roles'
13
14
id = Column(Integer, primary_key=True, autoincrement=True)
15
name = Column(String(40), unique=True)
16
17
18
19
class UserRolesMap(sa.Model):
20
__tablename__ = 'user_roles_map'
21
22
id_seq = Sequence(__tablename__ + "_id_seq")
23
id = Column(Integer(), id_seq, server_default=id_seq.next_value(),
24
unique=True, nullable=False)
25
user_id = Column(
26
Integer, ForeignKey('users.id'),
27
primary_key=True)
28
role_id = Column(
29
Integer, ForeignKey('roles.id'),
30
primary_key=True)
31
Advertisement
Answer
I didn’t find what I was looking for, so for now I just wrote it with joins:
JavaScript
1
7
1
query = db_session.query(Users)
2
.filter_by(**parameters)
3
.join(UserRolesMap, UserRolesMap.user_id == Users.id)
4
.filter(UserRolesMap.role_id.in_(roles_ids))
5
.group_by(Users)
6
.having(func.count(UserRolesMap.role_id) >= len(roles_ids))
7
where roles_ids was collected from Roles table before. And if you need user with only required roles you can replace “>=” with “==”.