带有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_by
和 distinct
是相同的。第一个-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的不同和限制的主要内容,如果未能解决你的问题,请参考以下文章