【问题标题】:Django ORM - LEFT JOIN with WHERE clauseDjango ORM - 带有 WHERE 子句的 LEFT JOIN
【发布时间】:2015-08-21 08:21:05
【问题描述】:

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

【问题讨论】:

  • 没有用户操作的用户是否应该包含在结果中?
  • @f43d65 - 是的,他们应该

标签: django django-orm django-1.8


【解决方案1】:

我和你有同样的问题。我还没有找到解决问题的任何适当方法,但我找到了一些修复方法。

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

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

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

【讨论】:

    猜你喜欢
    • 2011-01-30
    • 1970-01-01
    • 1970-01-01
    • 2017-02-20
    • 2017-05-05
    • 2014-05-25
    • 1970-01-01
    • 1970-01-01
    • 2017-09-09
    相关资源
    最近更新 更多