Django rest filter by serializermethodfield with custom filter

As declared in question title, i got task to filter results by field not presented in model but calculated by serializer.

Here is the view code:

class RecipeViewSet(ModelViewSet):
    queryset = Recipe.objects.all()
    permission_classes = [IsAdminOrAuthorOrReadOnly, ]
    serializer_class = RecipeInSerializer
    pagination_class = LimitPageNumberPagination
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['tags', ]
    filter_class = RecipeFilter

Serializer:

class RecipeOutSerializer(serializers.ModelSerializer):
    tags = ManyRelatedField(child_relation=TagSerializer())
    author = CustomUserSerializer()
    ingredients = serializers.SerializerMethodField()
    is_favorite = serializers.SerializerMethodField()
    is_in_shopping_cart = serializers.SerializerMethodField()

    class Meta:
        fields = '__all__'
        model = Recipe

    def get_ingredients(self, obj):
        ingredients = IngredientAmount.objects.filter(recipe=obj)
        return GetIngredientSerializer(ingredients, many=True).data

    def get_is_favorite(self, obj):
        request = self.context.get("request")
        if request.user.is_anonymous:
            return False
        return Favorite.objects.filter(recipe=obj, user=request.user).exists()

    def get_is_in_shopping_cart(self, obj):
        request = self.context.get("request")
        if not request or request.user.is_anonymous:
            return False
        return ShoppingCart.objects.filter(recipe=obj, user=request.user).exists()

And custom filter code:

class RecipeFilter(rest_framework.FilterSet):
    tags = ModelMultipleChoiceFilter(
        field_name='tags__slug',
        to_field_name="slug",
        queryset=Tag.objects.all()
    )

    favorite = BooleanFilter(field_name='is_favorite', method='filter_favorite')

    def filter_favorite(self, queryset, name, value):
        return queryset.filter(is_favorite__exact=True)

    class Meta:
        model = Recipe
        fields = ['tags', ]

Target is is_favorited field that return boolean value. I tried writing func in custom filter class that return queryset but didnt work, neither documentation helped me with examples. Hope for your help.

We can use queryset annotate:

from django.db import models
from rest_framework import serializers

class RecipeViewSet(ModelViewSet):
    def get_queryset(self):
        user = self.request.user
        user_id = user.id if not user.is_anonymous else None
        return Recipe.objects.all().annotate(
            total_favorite=models.Count(
                "favorite",
                filter=models.Q(favorite__user_id=user_id)
            ),
            is_favorite=models.Case(
                models.When(total_favorite__gte=1, then=True),
                default=False,
                output_field=BooleanField()
            )
        )


class RecipeOutSerializer(serializers.ModelSerializer)
    is_favorite = serializers.BooleanField(read_only=True)

    class Meta:
        model = Recipe
        fields = (
            # ...
            is_favorite,
        )

class RecipeFilter(rest_framework.FilterSet):
    favorite = BooleanFilter(field_name='is_favorite')
        
Back to Top