如果满足某些条件,则Django ORM查询排除按行分组(postgresql)

时间:2013-06-19 22:34:16

标签: django postgresql group-by distinct django-orm

这是一个应用程序,它启用了一些协作审核功能,类似于Stackoverflow。

当前状态

我有一个像这样的Django ORM查询:

versions = Version.objects.filter(is_reviewed=False).exclude(moderations__user__pk=request.user.id).order_by('content_type__id', 'object_id', '-number').distinct('content_type', 'object_id')

每个内容对象可以具有由用户创建的任意数量的版本。我在这里做的是获取来自其他用户的所有未经审核的版本,但每个内容对象只有最新版本。

问题

但是我想添加另一个排除条件:如果内容对象具有is_reviewed=True的较新版本,则不应返回以前的版本。应该退回未经审核的新版本。知道怎么做吗?

一些例子。我假设这里的所有版本都是针对相同的内容对象。

示例A

  1. “第一版” - 未审核
  2. “改进版” - 未审核
  3. “更多改进版” - 未审核
  4. 这应该返回第三行。按预期工作。

    示例B

    1. “第一版” - 已审核
    2. “改进版” - 未审核
    3. “更多改进版” - 未审核
    4. 这应该返回第三行。按预期工作。

      示例C

      1. “第一版” - 未审核
      2. “改进版” - 已审核
      3. “更多改进版” - 未审核
      4. 这应该返回第三行。按预期工作。

        示例D

        1. “第一版” - 未审核
        2. “改进版” - 未审核
        3. “更多改进版” - 已审核
        4. 这应该返回没有行。目前这不起作用。它将返回第二行,而不是它的目的。

          /编辑

          以下是我的模型:http://sql.nopaste.dk/p48981

1 个答案:

答案 0 :(得分:1)

我喜欢编写queryset方法来帮助解释我们正在尝试做什么,并从那里开始向后工作。

Version.objects.newest_by_content_type().unreviewed()

然后,我们只需要queryset / manager方法。 [注意:我正在使用Building a higher-level query API中描述的模式将这些方法添加到查询集中。

事实证明,这是你已经编写的代码,只是略有不同。

def newest_by_content_type(self):
    return self.order_by('content_type__id', 'object_id', '-updated').distinct('content_type', 'object_id')

def unreviewed(self):
    return self.filter(is_reviewed=False)

然后,您可以使用此查询集执行其他操作。例如,删除当前用户拥有的那些。

此答案的上一个版本使用superseded_bysupersedes自我关系讨论了另一种解决方案:

class Version(models.Model):
    supersedes = models.ForeignKey('Version', 
        related_name='superseded_by', null=True, blank=True)

然后,您可以通过以下方式获取所有未被取代的对象:

Version.objects.filter(superseded_by=None)

根据约束条件,对于被取代的关系使用OneToOneField可能是有意义的:如果版本最多只能被一个版本取代,并且版本可能只取代一个版本。例如,当尝试再次取代已被取代的对象时,也可以使用它和HTTP标头并返回412 Precondition Failed。这对于版本链“直”而言是有意义的,并且不允许分支。