我在Flask SQLAlchemy中有User和Room模型。我需要过滤如果房间存在用户[user1,user2,...]。过滤器必须准确。
这是我的模特:
room_users_table = db.Table(
'room_users',
db.metadata,
db.Column('user', db.Integer, db.ForeignKey('user.id')),
db.Column('room', db.Integer, db.ForeignKey('room.id'))
)
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(80))
last_name = db.Column(db.String(80))
password = db.Column(db.String(80))
email = db.Column(db.String(120), unique=True)
rooms = db.relationship(
"Room",
secondary=room_users_table,
back_populates="users"
)
class Room(db.Model):
__tablename__ = 'room'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), unique=True)
users = db.relationship(
"User",
secondary=room_users_table,
back_populates="rooms"
)
谢谢,我已经搜索了所有类似问题但无法找到答案。
答案 0 :(得分:0)
经过两天的研究,我有一个临时解决方案。
-- room_users is the "secondary" table in
-- sqlalchemy many to many between User and Room
select room.name, room_users.room
from room_users join room on room_users.room = room.id
where room_users.user in (1,2)
group by room_users.room having count(*) = 2;
在python中它将是这样的:
# members contains user emails
members = ['email1@mail.com', 'email2@mail.com']
db.session.query(Room.name).join(Room.users).filter(
User.email.in_(members)
).group_by(Room.id).having(
func.count(Room.id) == len(members)
).first()
答案 1 :(得分:0)
您可以使用relational division版本和其他一些过滤来实现此目的:
首先建立一个临时"表" (联合)您要搜索的电子邮件:
In [46]: emails = ['email1@mail.com', 'email2@mail.com']
In [47]: emails_union = db.union(*(db.select([db.literal(email).label('email')])
for email in emails)).alias()
这可能看起来有点不受欢迎,但它本质上形成了一个像这样的SQL UNION:
SELECT 'email1@mail.com' AS email
UNION
SELECT 'email2@mail.com' AS email
并给它一个别名。某些数据库可能支持从列表for example with Postgresql you could:
生成新关系的其他方法In [64]: from sqlalchemy.dialects.postgresql import array
In [65]: emails_relation = db.func.unnest(array(emails)).alias()
除法本身是使用双重否定或2个嵌套NOT EXISTS条件完成的:
In [48]: db.session.query(Room).\
...: filter(~db.session.query().select_from(emails_union).
...: filter(~Room.users.any(email=emails_union.c.email)).
...: exists(),
...: ~Room.users.any(User.email.notin_(emails))).\
...: all()
Out[48]: [<__main__.Room at 0x7fad4d238128>]
In [49]: [(r.name, [u.email for u in r.users]) for r in _]
Out[49]: [('room1', ['email1@mail.com', 'email2@mail.com'])]
该查询几乎回答了问题&#34;找到Room
&#34;中不存在此类电子邮件的Room.users
个问题。 - 找到包含所有给定电子邮件的房间 - 然后它应用第3个NOT EXISTS条件,过滤掉带有额外电子邮件的房间。如果没有它,查询也会返回 room2 ,其中包含电子邮件1,2和3。
搜索是针对这些数据进行的:
In [10]: users = [User(id=id_, email='email{}@mail.com'.format(id_))
...: for id_ in range(1, 10)]
In [11]: rooms = [Room(id=id_, name='room{}'.format(id_))
...: for id_ in range(1, 10)]
In [18]: db.session.add_all(users)
In [19]: db.session.add_all(rooms)
In [20]: for room, user1, user2 in zip(rooms, users, users[1:]):
...: room.users.append(user1)
...: room.users.append(user2)
...:
In [21]: rooms[1].users.append(users[0])
In [22]: db.session.commit()