Django查询使用.order_by()和.latest()

42

我有一个模型:

class MyModel(models.Model):
   creation_date = models.DateTimeField(auto_now_add = True, editable=False)

   class Meta:
      get_latest_by = 'creation_date'

我在我的视图中有一个查询,它执行以下操作:

instances = MyModel.objects.all().order_by('creation_date')

后来我想使用 instances.latest(),但它没有给我正确的实例,事实上它给了我第一个实例。只有当我将 order_by 设置为 -creation_date 或者从查询中实际删除 order_by 时,.latest() 才会给我正确的实例。当我手动使用 python manage.py shell 进行测试而不是在视图中进行测试时,也会出现这种情况。

所以现在我做的是在模型的元数据中列出 order_by = ['creation_date'] 而不在查询中使用它,并且这可以工作。

我本来期望 .latest() 始终根据(日期)(时间)字段返回最新的实例。请问是否正确,在查询中使用 order_by 时,.latest() 表现奇怪?


只有当我将 order_by 设置为 -creation_date,或者实际上从查询中删除 order_by,.latest() 才会给我正确的实例。您能解释一下这句话的意思吗?我不太理解... - Tariq Ahmed
4个回答

75
我本来期望.latest() 根据一个(日期)(时间)字段,总是返回最新的实例。根据文档,如果你的模型的Meta指定了get_latest_by,你可以略去field_name参数传递给latest()。Django会默认使用get_latest_by中指定的字段。这意味着当你调用MyModel.objects.latest()时,你将得到基于日期/时间字段的最新实例。我使用示例数据测试了你的代码,确实如此。
另外,你误解了latest()的工作方式。当在MyModel.objects上调用它时,它返回表中的最新实例。当在queryset上调用latest()时,则返回查询集中的第一个对象。你的查询集由按照creation_date升序排列的MyModel所有实例组成。因此,latest()在这个查询集上应该返回查询集第一行。这恰好是表中最旧的行。
要更好地理解,请查看为latest()所执行的查询语句。
Case 1:
from django.db import connection
MyModel.objects.latest()
print connection.queries[-1]['sql']

这将打印:

SELECT "app_mymodel"."id", "app_mymodel"."creation_date" FROM 
"app_mymodel" ORDER BY "app_mymodel"."creation_date" DESC LIMIT 1

注意按 creation_date DESC 的顺序和 LIMIT 子句。前者是由 get_latest_by 负责,而后者则是由 latest 贡献的。

现在,进入情况2:

MyModel.objects.order_by('creation_date').latest()
print connection.queries[-1]['sql']

打印

SELECT "app_mymodel"."id", "app_mymodel"."creation_date" FROM 
"app_mymodel" ORDER BY "app_mymodel"."creation_date" ASC LIMIT 1
注意,排序已更改为creation_date ASC。 这是显式order_by的结果。 LIMIT稍后会添加,感谢latest
让我们再看一下第三种情况:在objects.latest()中显式指定field_name
MyModel.objects.latest('id')
print connection.queries[-1]['sql']

显示

SELECT "app_mymodel"."id", "app_mymodel"."creation_date" FROM "app_mymodel"
ORDER BY "app_mymodel"."id" DESC LIMIT 1

2
非常感谢您的出色解释!您已经让我非常清楚地了解了.latest()的工作原理以及我一直做错的事情。特别是SQL技巧非常有帮助。这在将来肯定会派上用场。 - Heyl1
1
如果我只想获取最近插入的一列数据怎么办?比如:select "app_mymodel"."id" FROM "app_mymodel" ORDER BY "app_mymodel"."creation_date" ASC LIMIT 1,相应的 Python 代码是什么? - Héléna
现在你应该更新你的回答,因为MyModel.objects.latest()MyModel.objects.all().latest(')返回相同的结果。 - Tariq Ahmed

9

我猜这是Django中一个已知错误,在1.3版本后已经修复。


0

如果我们有id或日期的值

post_id = BlogPost.objects.get(id=id)
try:
   previous_post = BlogPost.objects.all().order_by('id')[post_id.id-2:post_id.id-1]
except:   
   previous_post = None
try:
   next_post = BlogPost.objects.all().order_by('id')[post_id.id:post_id.id+1]
except:
  next_post = None

它对我起作用,即使缺少一个 ID,它也会选择接下来或上一个值。


0

这对我起作用了

latestsetuplist = SetupTemplate.objects.order_by('-creationTime')[:10][::1]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接