如何从序列化程序输出中动态删除字段

How to dynamically remove fields from serializer output

我正在使用 Django Rest 框架开发 API,我想从序列化程序中动态删除字段。问题是我需要根据另一个字段的值删除它们。我怎么能那样做? 我有一个像这样的序列化器:

class DynamicSerliazer(serializers.ModelSerializer):
    type = serializers.SerializerMethodField()
    url = serializers.SerializerMethodField()
    title = serializers.SerializerMethodField()
    elements = serializers.SerializerMethodField()

    def __init__(self, *args, **kwargs):
        super(DynamicSerliazer, self).__init__(*args, **kwargs)
        if self.fields and is_mobile_platform(self.context.get('request', None)) and "url" in self.fields:
            self.fields.pop("url")

如您所见,我已经删除了 "url" 字段,具体取决于请求是否是从移动平台完成的。但是,我想根据 "type" 值删除 "elements" 字段。我应该怎么做?

提前致谢

可以customize the serialization behavior通过覆盖序列化器中的to_representation()方法

class DynamicSerliazer(serializers.ModelSerializer):

    def to_representation(self, obj):
        # get the original representation
        ret = super(DynamicSerializer, self).to_representation(obj)

        # remove 'url' field if mobile request
        if is_mobile_platform(self.context.get('request', None)):
            ret.pop('url')

        # here write the logic to check whether `elements` field is to be removed 
        # pop 'elements' from 'ret' if condition is True

        # return the modified representation
        return ret 

您可以创建多个序列化器并在视图中选择合适的一个

class IndexView(APIView):
    def get_serializer_class(self):
        if self.request.GET['flag']:
            return SerializerA
        return SerializerB

使用继承让序列化器变干。

我的问题和你的有点相似,我用继承解决了。

class StaticSerializer(serializers.ModelSerializer):

    class Meta:
        model = StaticModel
        fields = (
            'first_name', 'last_name', 'password', 'username',
            'email'
        )


class DynamicSerializer(StaticSerializer):

    class Meta:
        model = StaticModel
        fields = (
            'first_name',
        )

解决方案(ViewSet 混合)

我已经通过编写自己的 ViewSet mixin 解决了这个问题。它提供了非常简单和 DRY 的方法来根据请求操作覆盖序列化程序。

class ActionBasedSerializerClassMixin(viewsets.ModelViewSet):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def get_serializer_class(self):
        attr_name = f'{self.action}_serializer_class'
        if hasattr(self, attr_name):
            serializer_class = getattr(self, attr_name)
            self.serializer_class = serializer_class
        return super().get_serializer_class()

用法

要在您的视图集中使用此 mixin 继承自它(它必须在 ModelViewSet 父级之前)。 默认序列化程序始终用作回退 要在列表操作上使用不同的序列化程序,只需在您的视图集中设置属性 list_serializer_class:

    class MyViewSet(ViewSet):
        serializer_class = MySerializer
        list_serializer_class = MyListSerializer

使用此代码,当操作为 'list' 时,您将拥有 MyListSerializer,而对于所有其他操作,您将拥有 MySerializer。 相同的模式适用于所有其他操作类型:列出、创建、检索、更新、partial_update、销毁。 您只需附加 _serializer_class 即可获得所需的属性名称。

序列化程序应该是什么样子

class MySerializer(serializers.ModelSerializer):
    some_reverse_rel = MyOtherSerializer(many=True, read_only=True)

    class Meta:
        model = MyModel
        fields = ['field1', 'field2', 'foo', 'bar', 'some_reverse_rel']

class MyListSerailizer(MySerializer):  # Note that we inherit from previous serializer
    some_reverse_rel = None  # Getting rid of reverse relationship

    class Meta(MySerializer.Meta):
        fields = ['foo', 'bar', 'field1']