如果我在长度为 1 的 QuerySet 上使用 first() 与 last(),为什么会得到不同的结果

Why do I get different results if I use first() vs last() on a QuerySet with the length of 1

在为方法编写测试用例时,我发现使用 my_queryset.first().my_annotated_value 与使用 my_queryset.last().[=31 时得到的结果不同=] 虽然 my_queryset.count() returns 1.

这里是相关的代码片段:

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() 的实现。我找不到任何解释此行为的内容。

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

在您的 get_best_matches 方法中,您在循环遍历查询集时对其求值,并为查询集中的每个项目修改 shop.max_match_percentage

当您调用 first() 时,Django return 是计算的查询集中的第一项。

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

如您所见,在从模型管理器方法 return 调用查询集之前循环遍历并修改它可能不是一个好主意。如果查询集被克隆(例如,通过进一步的 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')

如果你真的需要 return 一个浮点数而不是小数字段,你可以使用 Cast.