I have a very simple model called Achievement
. In an algorithm, I reach a point in which I have a queryset for this model - named achievements
bellow. I got in a situation in which the method .first()
applied to this queryset outputs None
, even though there is an element in the queryset.
In summary,
achievements[0] # outputs an achievement
achievements.first() # None
achievements.count() # 1
How can this happen? There is no default ordering for this model.
When one calls .first()
on a queryset, the ordering of the queryset is important as that would decide what object is first. When the queryset has an order it is used, if it doesn't have one the primary key is automatically used for ordering. Quoting the documentation on first()
:
Returns the first object matched by the queryset, or None if there is no matching object. If the QuerySet has no ordering defined, then the queryset is automatically ordered by the primary key. This can affect aggregation results as described in Interaction with default ordering or order_by().
This automatic use of the primary key in the order, or let's say some other ordering that you provide will change how aggregates happen (As previously what may have been in a group will now not be in a group because of the order). Hence you observe what happens with your query.
Consider the following example (The Bar
model is not needed but that is what I already had for testing purposes):
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
Therefore instead of using .first
you can simply do the follows to get the proper result:
result = None
sliced_queryset = queryset[:1]
if sliced_queryset:
result = sliced_queryset[0]
print(result)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With