带有order_by的Django查询,对Postgresql的不同和限制

Posted

技术标签:

【中文标题】带有order_by的Django查询,对Postgresql的不同和限制【英文标题】:Django query with order_by, distinct and limit on Postgresql 【发布时间】:2015-07-17 00:35:54 【问题描述】:

我有以下:

class Product(models.Model):
    name = models.CharField(max_length=255)

class Action(models.Model):
    product = models.ForeignKey(Product)
    created_at = models.DateTimeField(auto_now_add=True)

我想检索由 created_at DESC 排序的最近 10 个具有不同产品的操作。

以下是接近结果但仍然错过排序:

Action.objects.all().order_by('product_id').distinct('product_id')[:10]

【问题讨论】:

您是否尝试过颠倒您的order_by()distinct() 调用顺序? Django 的文档指出:“指定字段名时,必须在 QuerySet 中提供 order_by(),并且 order_by() 中的字段必须以 distinct() 中的字段开头,同命令。”。 docs.djangoproject.com/en/1.8/ref/models/querysets/… 您的订单通过product_id,而不是created_at。为什么? 因为要让 distinct('product_id') 工作,您必须在 order_by 子句中指定它。 但是,您可以为 order_by 设置多个参数。根据您刚刚提供的链接的示例,您似乎可以这样做:.order_by('product_id', 'created_at').distinct('product_id') 【参考方案1】:

您的解决方案似乎做得太多。它还将导致 2 个单独的 SQL 查询。这可以正常工作,并且只需要一个查询:

action_ids = Action.objects.order_by('product_id', '-created_at')\
    .distinct('product_id').values_list('id', flat=True)

result = Action.objects.filter(id__in=action_ids)\
    .order_by('-created_at')[:10]

【讨论】:

使用 `values_list' 是个好主意,因为它可以保存一个查询。我相应地编辑了我的答案。 你试过运行我的代码吗?第一行将运行良好,因为第一个 order_bydistinct 是相同的。第一个-created_at 确保您只能获得每个产品的最新操作(此时它们的顺序错误)。第二行只是将操作按正确的顺序排列,但仍将其保留在同一个查询中。 感谢您的解释,您是对的!您的代码完美运行,比我的更好!谢谢:) 我仍然不明白为什么我们不能像Action.objects.order_by('product_id', '-created_at').distinct('product_id')[:10] 这样只在一行中做到这一点。我之所以这么说是因为我正在尝试使用.distinct(...).filter(...),而且过滤器似乎在不同之前“执行”了......【参考方案2】:

编辑:此解决方案有效,但 Ross Lote 的更清洁

这就是我最终做到的方式,使用Django Aggregation:

from django.db.models import Max
    
actions_id = Action.objects.all().values('product_id') \
    .annotate(action_id=Max('id')) \
    .order_by('-action_id')[:10] \
    .values_list('action_id', flat=True)

result = Action.objects.filter(id__in=actions_id).order_by('-created_at')

通过设置values('product_id'),我们对product_id进行group by

使用annotate(),我们只能在values()annotate() 中使用的字段上使用order_by。由于对于每个操作,created_at 字段都会自动设置为 now,因此在 created_at 上的排序与在 id 上的排序相同,使用 annotate(action_id=Max('id')).order_by('-action_id') 是正确的方法。

最后,我们只需要对查询进行切片 [:10]

希望这会有所帮助。

【讨论】:

以上是关于带有order_by的Django查询,对Postgresql的不同和限制的主要内容,如果未能解决你的问题,请参考以下文章

Django数据查询中对字段进行排序

Django数据查询中对字段进行排序

django对数据查询结果进行排序的方法

如何在 django 中包含条件 order_by?

Django:Order_by 多个字段

Django Graphene Relay order_by(OrderingFilter)