2
votes

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()
1

1 Answers

1
votes

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