如何在Django中执行双连接关系的查询?
应该有办法通过ORM来完成这个查询,但我现在没有找到。
设置情况
我在建模的内容是:一个租户可以住多个房间,而一个用户可以拥有多个房间。所以房间有一个指向租户的外键(FK)和一个指向用户的外键。房间的维护者也可能是一个不同的用户。
也就是说,我有这些(简化的)模型:
class Tenant(models.Model):
name = models.CharField(max_length=100)
class Room(models.Model):
owner = models.ForeignKey(User)
maintainer = models.ForeignKey(User)
tenant = models.ForeignKey(Tenant)
问题
给定一个租户,我想要找出拥有他们所住房间的用户。
相关的SQL查询应该是:
SELECT auth_user.id, ...
FROM tenants_tenant, tenants_room, auth_user
WHERE tenants_tenant.id = tenants_room.tenant_id
AND tenants_room.owner_id = auth_user.id;
从相关的用户对象中获取任何单个值可以通过,比如说,my_tenant.rooms.values_list('owner__email', flat=True)
来完成,但获取用户的完整查询集让我感到困惑。
通常,解决这个问题的一种方法是在我的Tenant
模型上设置一个指向User
的ManyToMany
字段,并使用TenantRoom
作为“中介”模型。不过在这种情况下,这样做不太合适,因为TenantRoom
模型还有一个指向User
的第二个(无关的)外键(见“限制”)。而且这似乎会让租户模型变得杂乱。
使用my_tenant.rooms.values_list('user', flat=True)
接近目标,但返回的是用户ID的值列表,而不是实际的用户对象查询集。
问题
所以:有没有办法通过ORM,只用一个查询就获取实际模型实例的查询集?
编辑
如果确实没有办法直接通过ORM在一个查询中做到这一点,那么实现我想要的结果的最佳方式是什么(在性能、习惯用法、可读性等方面的组合)?我看到的选项有:
子查询
users = User.objects.filter(id__in=my_tenant.rooms.values_list('user'))
通过Python进行子查询(见性能考虑,了解背后的原因)
user_ids = id__in=my_tenant.rooms.values_list('user') users = User.objects.filter(id__in=list(user_ids))
原始SQL:
User.objects.all("""SELECT auth_user.* FROM tenants_tenant, tenants_room, auth_user WHERE tenants_tenant.id = tenants_room.tenant_id AND tenants_room.owner_id = auth_user.id""")
其他...?
1 个回答
3
正确的做法是使用 related_name
:
class Tenant(models.Model):
name = models.CharField(max_length=100)
class Room(models.Model):
owner = models.ForeignKey(User, related_name='owns')
maintainer = models.ForeignKey(User, related_name='maintains')
tenant = models.ForeignKey(Tenant)
这样你就可以这样做:
jrb = User.objects.create(username='jrb')
bill = User.objects.create(username='bill')
bob = models.Tenant.objects.create(name="Bob")
models.Room.objects.create(owner=jrb, maintainer=bill, tenant=bob)
User.objects.filter(owns__tenant=bob)