I am trying to do aggregation of a model (Reply) by geographic area (Hexgrid_10km2). Similar to SQL 'group by'. I am using .values(Hexgrid_10km) to do this. I am then annotating some numbers onto this. I am getting the following error in the serialization to geojson:

'dict' object has no attribute 'hexgrid_10km2'

But Reply does a have field called 'hexgrid_10km2'.

I can't figure this out. I've tried other ways to serialize on stackoverflow, but always seem to get an error. Does anybody know I am doing wrong? Many thanks!!

I am using djangorestframework-gis

Models

class Hexgrid_10km2(models.Model):
    lng = models.FloatField()
    lat = models.FloatField()
    polygon = models.MultiPolygonField(srid=4326)
    centroid = models.PointField(default=Point(0,0), srid=4326)

    def __str__(self):
        return f'lng: { self.lng } lat: {self.lat }'


class Animal (models.Model):
    name = models.CharField(max_length=200)


class Reply(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    reply_date = models.DateTimeField()
    animal = models.ForeignKey(Animal, on_delete=models.CASCADE)
    ability = models.IntegerField(default = 0)
    hexgrid_10km2 = models.ForeignKey(Hexgrid_10km2, on_delete=models.CASCADE, null=True, blank=True)

    @property
    def polygon_10km2(self):
        return self.hexgrid_10km2.polygon

Views

from rest_framework.views import APIView
from rest_framework.response import Response
from.models import Animal, Reply, Hexgrid_10km2
from django.db.models import Avg, Sum

class ReplyHeatmapAPIView(APIView):
    def get(self, request, pk):
        animal = get_object_or_404(Animal, pk=pk)
        relevant = Reply.objects.filter(animal=animal)
        most_recent = relevant.filter(
            reply_date=Subquery(
                (Reply.objects
                    .filter(user=OuterRef('user'))
                    .values('user')
                    .annotate(most_recent=Max('reply_date'))
                    .values('reply_date')[:1]
                )
            )
        final = most_recent.values('hexgrid_10km2')
            .annotate(average_ability=Avg('ability'), sum_ability=Sum('ability'))
        serializer = ReplyHeatmapSerializer(final, many=True)
        return Response(serializer.data)

Serializer

from rest_framework_gis.fields import GeometrySerializerMethodField
from rest_framework_gis.serializers import GeoFeatureModelSerializer

class ReplyHeatmapSerializer(GeoFeatureModelSerializer):
    """ A class to serialize hex polygons as GeoJSON compatible data """

    average_ability = serializers.FloatField()
    sum_ability = serializers.FloatField()

    polygon = GeometrySerializerMethodField()
    def get_polygon(self, obj):
        return obj.hexgrid_10km2.polygon

    class Meta:
        model = Reply
        geo_field = 'polygon'
        depth = 1
        id_field = False
        fields = ('id', 'average_abilty', 'sum_ability')

Traceback

Traceback (most recent call last):
  File "C:\Users\anton\OneDrive\Documents\django\karate-project-2.2\venv\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
    response = get_response(request)
  File "C:\Users\anton\OneDrive\Documents\django\karate-project-2.2\venv\lib\site-packages\django\core\handlers\base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "C:\Users\anton\OneDrive\Documents\django\karate-project-2.2\venv\lib\site-packages\django\core\handlers\base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Users\anton\OneDrive\Documents\django\karate-project-2.2\venv\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "C:\Users\anton\OneDrive\Documents\django\karate-project-2.2\venv\lib\site-packages\django\views\generic\base.py", line 71, in view
    return self.dispatch(request, *args, **kwargs)
  File "C:\Users\anton\OneDrive\Documents\django\karate-project-2.2\venv\lib\site-packages\rest_framework\views.py", line 505, in dispatch
    response = self.handle_exception(exc)
  File "C:\Users\anton\OneDrive\Documents\django\karate-project-2.2\venv\lib\site-packages\rest_framework\views.py", line 465, in handle_exception
    self.raise_uncaught_exception(exc)
  File "C:\Users\anton\OneDrive\Documents\django\karate-project-2.2\venv\lib\site-packages\rest_framework\views.py", line 476, in raise_uncaught_exception
    raise exc
  File "C:\Users\anton\OneDrive\Documents\django\karate-project-2.2\venv\lib\site-packages\rest_framework\views.py", line 502, in dispatch
    response = handler(request, *args, **kwargs)
  File "C:\Users\anton\OneDrive\Documents\django\karate-project-2.2\karate2\animals\api\views.py", line 109, in get
    return Response(serializer.data)
  File "C:\Users\anton\OneDrive\Documents\django\karate-project-2.2\venv\lib\site-packages\rest_framework_gis\serializers.py", line 20, in data
    return super(ListSerializer, self).data
  File "C:\Users\anton\OneDrive\Documents\django\karate-project-2.2\venv\lib\site-packages\rest_framework\serializers.py", line 260, in data
    self._data = self.to_representation(self.instance)
  File "C:\Users\anton\OneDrive\Documents\django\karate-project-2.2\venv\lib\site-packages\rest_framework_gis\serializers.py", line 28, in to_representation
    ("features", super(GeoFeatureModelListSerializer, self).to_representation(data))
  File "C:\Users\anton\OneDrive\Documents\django\karate-project-2.2\venv\lib\site-packages\rest_framework\serializers.py", line 677, in to_representation
    return [
  File "C:\Users\anton\OneDrive\Documents\django\karate-project-2.2\venv\lib\site-packages\rest_framework\serializers.py", line 678, in <listcomp>
    self.child.to_representation(item) for item in iterable
  File "C:\Users\anton\OneDrive\Documents\django\karate-project-2.2\venv\lib\site-packages\rest_framework_gis\serializers.py", line 116, in to_representation
    feature["geometry"] = field.to_representation(geo_value)
  File "C:\Users\anton\OneDrive\Documents\django\karate-project-2.2\venv\lib\site-packages\rest_framework_gis\fields.py", line 101, in to_representation
    value = super(GeometrySerializerMethodField, self).to_representation(value)
  File "C:\Users\anton\OneDrive\Documents\django\karate-project-2.2\venv\lib\site-packages\rest_framework\fields.py", line 1905, in to_representation
    return method(value)
  File "C:\Users\anton\OneDrive\Documents\django\karate-project-2.2\karate2\animals\api\serializers.py", line 61, in get_polygon
    return obj.hexgrid_10km2.polygon
AttributeError: 'dict' object has no attribute 'hexgrid_10km2'

UPDATE Amended code. The reason I am filtering by Replys and not Hexgrid is because Users can make multiple Replys for the same Animal. So I need to filter the most recent reply that a user made for a particular Animal. I understand that .values() is not made for grouping by, but how else can I do it? I thought about grouping by hex grid but failed as even making the query to get the first Reply per user took me ages to figure out.

1

There are 1 best solutions below

10
On

The traceback is already telling you the problem: you are trying to access an attribute of a dictionary. The .values() method creates a dictionary with the values of the fields of an instance in the queryset, and what you're passing to the serializer is a list of all these dictionaries of values instead of a normal queryset, so when it tries to access a field of an instance of the queryset, it raises an exception because dictionaries have no attributes. You can't pass a dictionary to the serializer, it must receive a queryset.

To solve this, you have to erase .values() from the view, and you can just use polygon_10km2 as a field in the serializer, since it is a property of the model, to obtain what you want.