Django rest filter by serializermethodfield с пользовательским фильтром

Как заявлено в заголовке вопроса, у меня есть задача фильтровать результаты по полю, не представленному в модели, но вычисляемому сериализатором.

Вот код представления:

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

И код пользовательского фильтра:

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', ]

Целью является поле is_favorited, которое возвращает булево значение. Я пытался написать func в классе пользовательского фильтра, который возвращает queryset, но ничего не получилось, ни документация не помогла мне с примерами. Надеюсь на вашу помощь.

Мы можем использовать аннотацию queryset:

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')
        
Вернуться на верх