4

在为一个方法编写测试用例时,我发现如果我使用 my_queryset.first().my_annotated_value 与使用 my_queryset.last().my_annotated_value 时会得到不同的结果,尽管 my_queryset.count() 返回 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

在我运行的外壳中:

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')

我有不同的 django 应用程序用于shops,matches和.employeescustomers

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

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

4

1 回答 1

5

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

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

当您调用时last(),Django尝试返回反向查询集的第一项。这是一个新的查询集,会导致新的数据库查找。你没有设置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.

于 2017-06-02T16:06:54.043 回答