2

How would I filter results based on a computed field from a Serializer? I tried treating it like any other field, but django doesn't like it.

Serializer

class ImageSerializer(serializers.ModelSerializer):
    is_annotated = serializers.SerializerMethodField('has_annotation')

    class Meta:
        model = Image
        fields = '__all__'

    @staticmethod
    def has_annotation(image):
        return image.annotation_set.count() > 0

View

class ImageViewSet(viewsets.ModelViewSet):
    serializer_class = ImageSerializer
    lookup_field = 'id'
    permission_classes = [
        Or(IsAdmin),
        IsAuthenticated
    ]

    def get_queryset(self):
        queryset = Image.objects

        is_annotated_filter = self.request.query_params.get('is_annotated', None)
        if is_annotated_filter is not None:
            queryset = queryset.filter.annotate(
                cnt=Count('annotation')).filter(cnt__gte=1)

        queryset_order = get_queryset_order(self.request)
        return queryset.order_by(queryset_order).all()
waspinator
  • 6,464
  • 11
  • 52
  • 78

1 Answers1

1

I think you misunderstand: 1) serializer MethodField which major purpose is READ-ONLY and the serializer is not about to be used in filtering queryset.

I will filter it more like these:

from django.db.models import Count

queryset.filter.annotate(
    cnt=Count('annotation_set')
).filter(cnt__gte=1)

but... you can go even better:

1) just annotate your queryset e.g in your ViewSet

from django.db.models import Count
queryset = Image.objects.annotate(
    cnt_annotations=Count('annotation_set')
)

2) then in serializer do something like these:

@staticmethod
def has_annotation(image):
    if hasattr(obj, 'has_annotation'):
        return bool(obj.cnt_annotations)
    else:
        return None
andilabs
  • 22,159
  • 14
  • 114
  • 151
  • I'm getting `'function' object has no attribute 'annotate'` when I try to call it on `filter`. I think you're right, I was confused about what SerializerMethodField does. All I'm trying to do is filter results based on whether the image has annotations or not. – waspinator Mar 10 '18 at 00:05
  • 1
    first .annotate(some_new_field=some_logic) and then call .filter(some_new_field=foo) – andilabs Mar 10 '18 at 00:08