Search code examples
pythondjangogeodjango

Annotating Django queryset not returning annotations using backwards foreign key. Using geodjango


I have a mysterious problem where annotations are not showing up on queryset using backwards foreign key. Using Django 2.2.

Models

from django.contrib.gis.db import models

class Hexgrid_10km2(models.Model):
    polygon = gismodels.MultiPolygonField(srid=4326)


class Reply(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    reply_date = models.DateTimeField()
    ability = models.FloatField(default = 0)
    hexgrid_10km2 = models.ForeignKey(Hexgrid_10km2, related_name='replies', on_delete=models.CASCADE, null=True, blank=True)

Problem

I am first filtering the Hexgrid_10km2 to only those containing Replies as so:

most_recent = Reply.objects.filter(
    reply_date=Subquery(
        (Reply.objects
            .filter(user=OuterRef('user'))
            .values('user')
            .annotate(most_recent=Max('reply_date'))
            .values('reply_date')[:1]
        )
    )
)
hex_qs = Hexgrid_10km2.objects.filter(replies__in=most_recent)

>>> hex_qs
<QuerySet [<Hexgrid_10km2: Hexgrid_10km2 object (197028)>, <Hexgrid_10km2: Hexgrid_10km2 object (197028)>]>

I check to see that they do contain replies as so:

>>> hex_qs.aggregate(Sum('replies__ability'))
{'replies__ability__sum': 2.0}

Now the mystery...

>>> hex_qs.annotate(avg_ability=Avg('replies__ability'))
<QuerySet [<Hexgrid_10km2: Hexgrid_10km2 object (197028)>]>

Where is the annotation? Does it have something to do with geodjango which I am using to build the models? I am feeling like a fool. Many thanks for your help as am completely stuck.


Solution

  • If you .annotate(..) then you add an attribute to the objects that arise from that queryset.

    When Django prints a queryset, it makes use of repr(..) to print the (first) objects. So that means that unless the __repr__ method (or __str__ method) are implemented to print attributes, it will not show up.

    You thus can for example access the annotation of the first object with:

    hex_qs[0].avg_ability