Reputation: 193
I want to be able to query through multiple many to many relationships in SQLAlchemy.
I have Users, which are associated to Groups, which are associated to Roles. All associations are many-to-many. I want to get the list of Roles that are associated with the User through the Groups.
Here are my striped down models:
class User(Model):
id = Column(Integer)
groups = relationship('Group', secondary=user_group)
class Group(Model):
id = Column(Integer)
roles = relationship('Role', secondary=role_group)
class Role(Model):
id = Column(Integer)
I have a rough idea of what SQL would be used:
select distinct role.* from role, role_group
where role.id = role_group.role_id
and role_group.group_id in
(select "group".id from "group", user_group
WHERE user_group.user_id = 1
and "group".id = user_group."group.id")
But I am having a hard time figuring out how to translate that into SQLAlchemy...and I am not sure even if the SQL here is the best way to do this or not.
Upvotes: 2
Views: 1419
Reputation: 2050
Try this
s = Session()
s.query(Role).join(User.groups).join(Group.roles).filter(User.id==1).all()
it will generate following sql query
SELECT role.id AS role_id
FROM user
JOIN user_group AS user_group_1 ON user.id = user_group_1.user_id
JOIN "group" ON "group".id = user_group_1.group_id
JOIN role_group AS role_group_1 ON "group".id = role_group_1.group_id
JOIN role ON role.id = role_group_1.role_id
WHERE user.id = 1;
however sqlalchemy will only return distinct roles (if you will query Role.id then sqlalchemy will return what sql query actually returns).
Upvotes: 3
Reputation: 193
I did find a solution to my own question that would generate close to the SQL I was looking for. However, I believe zaquest's answer is much better and will be using that instead. This is just for posterity's sake.
Here is the query I came up with:
s = Session()
s.query(Role).join(Role.groups).filter(
Group.id.in_(s.query(Group.id).join(User.groups)
.filter(User.id == self.id)))
This would generate the following SQL
SELECT role.id AS role_id
FROM role JOIN role_group AS role_group_1 ON role.id = role_group_1.role_id
JOIN "group" ON "group".id = role_group_1.group_id
WHERE "group".id IN
(SELECT "group".id AS group_id
FROM "user" JOIN user_group AS user_group_1 ON "user".id = user_group_1.user_id
JOIN "group" ON "group".id = user_group_1."group.id"
WHERE "user".id = :id_1)
This is similar to the SQL I was looking for. The key that I forgot about was that I can use another query as part of the in
operator in SQLAlchemy.
Upvotes: 0