DRF 如何在嵌套的序列化器关系中选择特定的字段来显示? (没有额外的序列化器)

问题描述 投票:0回答:4

我有一个序列化器

class CategoryListSerializer(serializers.ModelSerializer):
class Meta:
    model = Category
    fields = ["id", "name", "name_en", "about", "parent",]

用在两个地方:

  1. All Categories API:用于查看有关类别的丰富详细信息。
  2. All Posts API:仅用于知道类别的名称。

在我的帖子序列化程序中,我使用了:

class PostListSerializer(serializers.ModelSerializer):
    categories = CategoryListSerializer(many=True, )

    class Meta:
        model = Post
        fields = ["id", "title", "description", "publish_date", "thumbnail", "owner", "categories", ]

在我的 Post ViewSet 中:

class PostViewSet(ReadOnlyModelViewSet):
    queryset = Post.objects.all().filter(is_published=True)
    serializer_class = PostListSerializer

这将返回

CategoryListSerializer
中提到的所有类别详细信息的所有帖子,因为它应该是。

问题:

我希望

PostListSerializer
只返回相关类别中的“名称”字段,而不必定义另一个仅选择“名称”字段的
CategorySimpleSerializer
。 (我仍然需要另一个 API 中的
CategoryListSerializer
字段)

可以吗?

注意: 这只是一个例子,我会有更多的用例,如果我必须创建许多自定义的“待嵌套”序列化器,我想提前知道,以避免暴露一些不必要的数据一些 API。如果稍后需要更改模型或 API,似乎有很多冗余更新工作。

django serialization django-rest-framework drf-queryset
4个回答
4
投票

正如@mtzd 在评论中提到的那样:

创建一个通用的动态序列化程序类(如 DRF 文档中)有效!

My Category Serializer 现在看起来像这样:

class DynamicFieldsCategorySerializer(serializers.ModelSerializer):
    def __init__(self, *args, **kwargs):
        # Don't pass the 'fields' arg up to the superclass
        fields = kwargs.pop('fields', None)

        # Instantiate the superclass normally
        super().__init__(*args, **kwargs)

        if fields is not None:
            # Drop any fields that are not specified in the `fields` argument.
            allowed = set(fields)
            existing = set(self.fields)
            for field_name in existing - allowed:
                self.fields.pop(field_name)


class CategoryListSerializer(DynamicFieldsCategorySerializer):
    class Meta:
        model = Category
        fields = [ "name", "name_en", "about",]

PostListSerializer
类别变量中,我只添加了字段属性:

categories = CategoryListSerializer(many=True, fields=['name',])

所以现在我可以管理向我使用的每个 View API 显示的字段,从我可以修改/更新一次的单一模型序列化程序。


1
投票

您应该使用下面描述的序列化程序来处理您的用例。

class PostListSerializer(serializers.ModelSerializer):
    categories = serializers.SerializerMethodField('get_categories')

    class Meta:
        model = Post
        fields = ["id", "title", "description", "publish_date", "thumbnail", "owner", "categories", ]
        
    def get_categories(self, obj):
        return obj.categories.all().values("name")

你还需要优化你的

Post.objects.all().filter(is_published=True)
Post.objects.filter(is_published=True).select_related("categories")


0
投票
class Meta:
    read_only_fields = (
        "id",
        "slug",
    )

0
投票

在您的类别模型中,您可以定义一个返回类别名称的方法,如下所示

class Category(models.Model):
     name=models.CharField()
     ``rest of attributes``
     def __str__(self):
       return self.name

在您的序列化程序中,如文档中所述stringrelatedfield

class PostListSerializer(serializers.ModelSerializer):
  categories = serializers.StringRelatedField(many=True)

  class Meta:
     model = Post
     fields = ["id", "title", "description", "publish_date", "thumbnail", 
              "owner", "categories", ]

在 PostViewset 中,因为您只需要只读模式,不需要更多修改,但如果它将用于创建帖子,您应该在 serializer.save() 方法中发送类别 ID,如下所示:

def create(self, request):
    serializer = self.get_serializer(data=request.data)
    category = Category.objects.get(pk=request.data['category'])
    if serializer.is_valid():
        serializer.save(category=category) 
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
© www.soinside.com 2019 - 2024. All rights reserved.