0

我已经在这里发表了与这个问题相关的前一篇文章,但因为这是一个相关但的问题,我认为最好再为它发表一篇文章。

我正在使用 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 子句,但对于我的生活,我找不到如何做到这一点。我查看了文档,查看了源代码,但没有找到答案。我(非常)确定这是可以做到的,我只是不知道怎么做。谁能指出我正确的方向?任何帮助将不胜感激。非常感谢!

4

1 回答 1

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']
    

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

  • 解决问题的另一种方法是使用extraQuerySet API 的方法。这是Timmy O'Mahony的这篇博客文章中详细介绍的方法。

    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()])
    })
    

    但是,此方法依赖于使用 SQL 表名调用数据库,这非常不符合 Django - 如果您更改db_table您的数据库之一或db_column它们的列之一,此代码将不再工作。它虽然只需要 2 个查询,第一个获取有效列表,userAction另一个将它们汇总给匹配的用户。

于 2015-11-19T10:13:42.513 回答