Django ORM 的`.first` 可以为非空查询集返回`None` 吗?

问题描述

我有一个非常简单的模型,名为 Achievement。在一个算法中,我达到了一个点,我有一个这个模型的查询集 - 命名为 achievements bellow。我遇到了一种情况,即应用到此查询集的方法 .first() 输出 None,即使查询集中有一个元素。

总而言之,

achievements[0] # outputs an achievement
achievements.first() # None
achievements.count() # 1

怎么会这样?此型号没有认排序。

解决方法

当在查询集上调用 .first() 时,查询集的排序很重要,因为这将决定首先是哪个对象。当查询集有一个订单时,它被使用,如果它没有一个,主键将自动用于排序。在 first() 上引用 documentation

返回查询集匹配的第一个对象,如果有则返回 没有匹配的对象。如果 QuerySet 没有定义排序, 然后查询集自动按主键排序。这 如Interaction with default ordering or order_by()中所述,可能会影响聚合结果。

在订单中自动使用主键,或者假设您提供的其他一些排序将改变聚合的发生方式(如前所述,由于顺序,可能已经在组中的现在不会在组中) .因此,您可以观察查询发生的情况。

考虑以下示例(Bar 模型不是必需的,但我已经用于测试目的):

class Bar(models.Model):
    value = models.IntegerField()


class ForeignFoo(models.Model):
    bar = models.ForeignKey(Bar,on_delete=models.CASCADE)
    int_field = models.IntegerField(default=0)



# Queries start here
b1 = Bar.objects.create(value=1)
b2 = Bar.objects.create(value=2)

ForeignFoo.objects.create(bar=b1,int_field=1) # pk 1
ForeignFoo.objects.create(bar=b2,int_field=1) # pk 2
ForeignFoo.objects.create(bar=b1,int_field=1) # pk 3

queryset = ForeignFoo.objects.values('bar').annotate(total=Sum('int_field')).filter(total__gt=1)
print(queryset.first()) # None
print(queryset[0]) # Prints an object

因此,您可以简单地执行以下操作来获得正确的结果,而不是使用 .first

result = None
sliced_queryset = queryset[:1]
if sliced_queryset:
    result = sliced_queryset[0]
print(result)