【问题标题】:Why do I get different results if I use first() vs last() on a QuerySet with the length of 1如果我在长度为 1 的 QuerySet 上使用 first() 与 last() 为什么会得到不同的结果
【发布时间】:2017-11-04 01:36:33
【问题描述】:

在为某个方法编写测试用例时,我发现使用 my_queryset.first().my_annotated_value 与使用 my_queryset.last().my_annotated_value 时得到的结果不同,尽管 my_queryset.count() 返回 1。

这里是相关代码sn-p:

class ShopManager(models.Manager):

def get_best_matches(self, customer):

    shops = super(ShopManager, self).get_queryset().filter(employees__matches__customer=customer).annotate(max_match_percentage=Coalesce(Max('employees__matches__match_value'), 0)).order_by('-max_match_percentage')

    for shop in shops:
        shop.max_match_percentage = float(shop.max_match_percentage) * 100.0

    return shops

在我运行的 shell 中:

shops = Shop.objects.get_best_matches(customer=Customer_A)
shops.count() # returns 1

shops.first().max_match_percentage # returns 73.9843
shops.last().max_match_percentage # returns Decimal('0.739843')

我有 shopsmatchesemployeescustomers 的不同 django 应用程序。

我搜索了几个小时并检查了 django 文档中 first() 和 last() 的实现。我找不到任何可以解释这种行为的东西。

为什么值不同,到底发生了什么?我做错了什么还是这是一个错误?

【问题讨论】:

  • 您可以尝试以相反的顺序运行shops.first().max_match_percentageshops.last().max_match_percentage。结果是什么
  • @Tushortz 我刚试过,结果是一样的。 shop.last().max_match_percentage 仍然返回 Decimal('0.739843')shop.first().max_match_percentage 仍然返回 73.9843
  • 您遇到了数据一致性问题。调用last 时再次克隆查询集,它不会在管理器方法中运行for 循环,因此您看到的是数据库中的当前值。请参阅 Alasdair 的回答。
  • 尽量不要在管理器中评估查询集。经理应该从数据库中检索数据,而不是更改它们。

标签: python django python-2.7 django-annotate


【解决方案1】:

在您的 get_best_matches 方法中,您在遍历查询集时评估它,并为查询集中的每个项目修改 shop.max_match_percentage

当您调用first() 时,Django 返回评估查询集中的第一项。

当你打电话给last(),Django attempts to return the first item of the reversed queryset。这是一个新的查询集,会导致新的数据库查找。你还没有为这个查询集设置shop.max_match_percentage,所以你从数据库中获取了小数。

如您所见,在从模型管理器方法返回之前遍历查询集并对其进行修改可能不是一个好主意。如果查询集被克隆(例如,通过进一步的filter()order_by() 或在本例中为last()),则更改将丢失。

您应该能够在查询集中进行乘以 100,而不是循环遍历它:

shops = super(ShopManager, self).get_queryset().filter(employees__matches__customer=customer).annotate(max_match_percentage=Coalesce(Max('employees__matches__match_value'), 0)*100).order_by('-max_match_percentage')

如果确实需要返回浮点数而不是小数字段,可以使用Cast

【讨论】:

    猜你喜欢
    • 2016-04-12
    • 2015-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-13
    • 1970-01-01
    • 2019-09-07
    • 2019-03-08
    相关资源
    最近更新 更多