选择相关模型:左连接,prefetch_related还是select_related?

时间:2017-04-11 09:22:51

标签: sql django

考虑到我有以下关系:

class House(Model):
  name = ...

class User(Model):
  """The standard auth model"""
  pass

class Alert(Model):
  user = ForeignKey(User)
  house = ForeignKey(House)
  somevalue = IntegerField()

  Meta:
    unique_together = (('user', 'property'),)

在一个查询中,我想获取房屋清单,以及当前用户是否有任何警报。

在SQL中我会这样做:

   SELECT *
     FROM house h
LEFT JOIN alert a
       ON h.id = a.house_id
    WHERE a.user_id = ?
       OR a.user_id IS NULL

我发现我可以使用prefetch_related来实现这样的目标:

p = Prefetch('alert_set', queryset=Alert.objects.filter(user=self.request.user), to_attr='user_alert')
houses = House.objects.order_by('name').prefetch_related(p)

上面的示例有效,但houses.user_alert是一个列表,而不是Alert对象。我每个房子每个用户只有一个警报,那么获取此信息的最佳方式是什么?

select_related似乎无法奏效。哦,当然我知道我可以在多个查询中管理这个问题,但我真的希望在一个问题中完成它,以及“Django方式”。

提前致谢!

1 个答案:

答案 0 :(得分:0)

如果您从多查询方法开始,然后尝试对其进行优化,则解决方案会更清晰。要获得每个房屋的user_alerts,您可以执行以下操作:

houses = House.objects.order_by('name')
for house in houses:
    user_alerts = house.alert_set.filter(user=self.request.user)

user_alerts查询集将导致查询集中每个房屋的额外查询。您可以使用prefetch_related来避免这种情况。

alerts_queryset = Alert.objects.filter(user=self.request.user)
houses = House.objects.order_by('name').prefetch_related(
    Prefetch('alert_set', queryset=alerts_queryset, to_attrs='user_alerts'),
)
for house in houses:
    user_alerts = house.user_alerts 

这将需要两个查询,一个用于房屋,一个用于警报。我认为您不需要在此处选择相关内容来获取用户,因为您已经可以访问self.request.user的用户。如果您愿意,可以将select_related添加到alerts_queryset

alerts_queryset = Alert.objects.filter(user=self.request.user).select_related('user')

在您的情况下,user_alerts将是一个空列表或包含一个项目的列表,因为您的unique_together约束。如果您无法处理列表,则可以遍历查询集一次,并设置house.user_alert

for house in houses:
    house.user_alert = house.user_alerts[0] if house.user_alerts else None