Django rest通过序列化器过滤器带有自定义过滤器的方法字段

yr9zkbsy  于 2022-11-18  发布在  Go
关注(0)|答案(1)|浏览(190)

正如在问题标题中声明的那样,我得到了一个任务,通过模型中没有出现但由序列化器计算的字段来过滤结果。
型号:

class Recipe(models.Model):
    tags = models.ManyToManyField(
        Tag,
        related_name='recipe_tags'
    )
    author = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name='author_recipes'
    )
    ingredients = models.ManyToManyField(
        Ingredient,
        related_name='recipe_ingredients'
    )
    name = models.CharField(max_length=200)
    image = models.ImageField()
    text = models.TextField()
    cooking_time = models.PositiveSmallIntegerField(
        validators=[MinValueValidator(1)]
    )

    class Meta:
        ordering = ("-id",)
        verbose_name = "Recipe"
        verbose_name_plural = "Recipes"

    def __str__(self):
        return self.name

下面是视图代码:

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

串行器:

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字段。我尝试在自定义过滤器类中编写返回queryset的函数,但没有工作,没有文档帮助我的例子。希望你的帮助。

2guxujil

2guxujil1#

我们可以使用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')

相关问题