Django ORM - LEFT JOIN与WHERE子句

时间:2015-06-06 11:52:18

标签: django django-orm django-1.8

我之前发过一篇与此问题相关的帖子here,但因为这是一个相关但新的问题,我认为最好再发一篇帖子。

我正在使用Django 1.8

我有一个User模型和一个UserAction模型。用户有类型。 UserAction有一个时间,表示动作花了多长时间,以及表示动作开始时间的start_time。它们看起来像这样:

class User(models.Model):
   user_type = models.IntegerField()

class UserAction:
   user = models.ForeignKey(User)
   time = models.IntegerField()
   start_time = models.DateTimeField()

现在我要做的是获取给定类型的所有用户及其操作的时间总和,可选择通过start_time进行过滤。

我正在做的是这样的事情:

# stubbing in a start time to filter by
start_time = datetime.now() - datetime.timedelta(days=2)
# stubbing in a type
type = 2
# this gives me the users and the sum of the time of their actions, or 0 if no 
# actions exist
q = User.objects.filter(user_type=type).values('id').annotate(total_time=Coalesce(Sum(useraction__time), 0)
# now I try to add the filter for start_time of the actions to be greater than or # equal to start_time
q = q.filter(useraction__start_time__gte=start_time)

现在它的作用当然是UserAction上的INNER JOIN,从而删除所有没有操作的用户。我真正想要做的是相当于我的LEFT JOIN与WHERE子句,但对于我的生活,我找不到如何做到这一点。我查看了文档,查看了源代码但未找到答案。我(很)确定这是可以完成的事情,我只是没有看到如何。有人能指出我正确的方向吗?任何帮助将非常感谢。非常感谢!

1 个答案:

答案 0 :(得分:0)

我和你有同样的问题。我还没有找到解决问题的正确方法,但我发现了一些修复。

  • 一种方法是循环遍历所有用户:

    q = User.objects.filter(user_type=type)
    for (u in q):
        u.time_sum = UserAction.filter(user=u, start_time__gte=start_time).aggregate(time_sum=Sum('time'))['time_sum']
    

    但是,此方法会在数据库中为每个用户进行查询。如果您没有很多用户,它可能会成功,但如果您拥有大型数据库,可能会非常耗时。

  • 解决问题的另一种方法是使用QuerySet API的extra方法。这是Timmy O'Mahony在this blog post中详述的方法。

    valid_actions = UserAction.objects.filter(start_time__gte=start_time)
    q = User.objects.filter(user_type=type).extra(select={
        "time_sum": """
        SELECT SUM(time)
        FROM userAction
        WHERE userAction.user_id = user.id
        AND userAction.id IN %s
        """ % (%s) % ",".join([str(uAction.id) for uAction in valid_actions.all()])
    })
    

    然而,如果您更改其中一个数据库的db_table或其中一列的db_column,则此方法依赖于使用SQL表名称调用数据库,这非常不是Django ,此代码将不再有效。它虽然只需要2个查询,但第一个用于获取有效userAction列表,另一个用于将它们与匹配用户相加。