Python Django REST框架-序列化可选字段

Python Django REST框架-序列化可选字段,python,django,serialization,django-rest-framework,Python,Django,Serialization,Django Rest Framework,我有一个具有可选字段的对象。我以这种方式定义了序列化程序: class ProductSerializer(serializers.Serializer): code = serializers.Field(source="Code") classification = serializers.CharField(source="Classification", required=False) 如果字段不存在,Irequired=False将绕过该字段。但是,文档中提到,这会影

我有一个具有可选字段的对象。我以这种方式定义了序列化程序:

class ProductSerializer(serializers.Serializer):
    code = serializers.Field(source="Code")
    classification = serializers.CharField(source="Classification", required=False)
如果字段不存在,I
required=False
将绕过该字段。但是,文档中提到,这会影响反序列化而不是序列化

我得到以下错误:

'Product' object has no attribute 'Classification'
当我尝试访问序列化实例的
.data
时,就会发生这种情况。(这不意味着是反序列化引起了这一问题吗?)

这种情况发生在没有
分类的实例上。如果我从序列化程序类中省略
分类
,它就可以正常工作


如何正确地执行此操作?使用可选字段序列化对象,也就是说。

序列化程序特意设计为使用一组固定的字段,因此您不容易有选择地删除其中一个键

如果字段不存在,可以使用返回字段值或
None
,或者根本不能使用序列化程序,只需编写一个直接返回响应的视图


REST framework 3.0更新
序列化程序。可以在实例化的序列化程序上修改字段
。当需要动态序列化程序类时,我可能会建议更改自定义
序列化程序中的字段。

Django REST Framework 3.0+
现在支持动态字段,请参阅--此方法定义序列化程序中的所有字段,然后允许您有选择地删除不需要的字段

或者,您也可以对模型序列化程序执行类似操作,在序列化程序init中处理Meta.fields:

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = ('code',)

    def __init__(self, *args, **kwargs):
        if SHOW_CLASSIFICATION: # add logic here for optional viewing
            self.Meta.fields = list(self.Meta.fields)
            self.Meta.fields.append('classification')
        super(ProductSerializer, self).__init__(*args, **kwargs)
不过,你必须问问汤姆,这是否是“正确的方法”,因为它可能不符合长期计划


Django REST框架<3.0
试着这样做:

class ProductSerializer(serializers.Serializer):
    ...
    classification = serializers.SerializerMethodField('get_classification')

    def get_classification(self, obj):
        return getattr(obj, 'classification', None)
多个序列化程序

    @action(detail=True, serializer_class=YourSerialzierClass)
    def teams_roles(self, request, pk=None):
        user = self.get_object()
        queryset = user.roles.all()
        serializer = self.get_serializer(queryset, many=True, excluded_fields=['user'])
        return Response(data=serializer.data)
另一种方法是使用不同的字段集创建多个序列化程序。一个序列化程序从另一个序列化程序继承并添加其他字段。然后,您可以使用
get\u serializer\u class
方法在视图中选择适当的序列化程序。下面是一个实际示例,演示了如果用户对象与请求用户相同,我如何使用此方法调用不同的序列化程序来呈现不同的用户数据

def get_serializer_class(self):
    """ An authenticated user looking at their own user object gets more data """
    if self.get_object() == self.request.user:
        return SelfUserSerializer
    return UserSerializer
从表示中删除字段

我在安全上下文中使用的另一种方法是删除
to_表示法
方法中的字段。定义一个类似于

def remove_fields_from_representation(self, representation, remove_fields):
    """ Removes fields from representation of instance.  Call from
    .to_representation() to apply field-level security.
    * remove_fields: a list of fields to remove
    """
    for remove_field in remove_fields:
        try:
            representation.pop(remove_field)
        except KeyError:
            # Ignore missing key -- a child serializer could inherit a "to_representation" method
            # from its parent serializer that applies security to a field not present on
            # the child serializer.
            pass
def to_representation(self, instance):
    """ Apply field level security by removing fields for unauthorized users"""
    representation = super(ProductSerializer, self).to_representation(instance)
    if not permission_granted: # REPLACE WITH PERMISSION LOGIC
        remove_fields = ('classification', ) 
        self.remove_fields_from_representation(representation, remove_fields)
    return representation
然后在序列化程序中,调用该方法,如

def remove_fields_from_representation(self, representation, remove_fields):
    """ Removes fields from representation of instance.  Call from
    .to_representation() to apply field-level security.
    * remove_fields: a list of fields to remove
    """
    for remove_field in remove_fields:
        try:
            representation.pop(remove_field)
        except KeyError:
            # Ignore missing key -- a child serializer could inherit a "to_representation" method
            # from its parent serializer that applies security to a field not present on
            # the child serializer.
            pass
def to_representation(self, instance):
    """ Apply field level security by removing fields for unauthorized users"""
    representation = super(ProductSerializer, self).to_representation(instance)
    if not permission_granted: # REPLACE WITH PERMISSION LOGIC
        remove_fields = ('classification', ) 
        self.remove_fields_from_representation(representation, remove_fields)
    return representation
这种方法简单而灵活,但代价是序列化有时不显示的字段。但这可能没关系。

在“这是一个可怕的黑客攻击,依赖于DRF和Django的具体实现细节,但它可以工作(至少现在是这样)”文件中,我使用了一种方法,在序列化程序上的“创建”方法实现的响应中包含一些额外的调试数据:

def create(self, validated_data)
    # Actual model instance creation happens here...
    self.fields["debug_info"] = serializers.DictField(read_only=True)
    my_model.debug_info = extra_data
    return my_model

这是一种临时方法,允许我使用可浏览的API来显示在创建过程中从特定远程服务接收的一些原始响应数据。将来,我倾向于保留此功能,但将其隐藏在创建请求中的“report debugging info”(报告调试信息)标志后面,而不是默认返回较低级别的信息。

为此,序列化程序具有
部分
参数。如果序列化程序初始化时,可以传递
partial=True
。如果您使用的是泛型或mixin,则可以重写
get\u serializer
函数,如下所示:

def get_serializer(self, *args, **kwargs):
    kwargs['partial'] = True
    return super(YOUR_CLASS, self).get_serializer(*args, **kwargs)
那就行了


注意:这允许所有字段都是可选的,而不仅仅是特定字段。如果您只需要详细信息,可以覆盖该方法(即更新)并为各个字段添加存在性验证。

下面描述的方法为我完成了这项工作。 非常简单,简单,对我来说很有用

使用的DRF版本=djangorestframework(3.1.0)


用于DRF 3的DynamicSerializer,它允许动态指定哪些字段将在序列化程序中使用,哪些字段将被排除,哪些字段是必需的

  • 创建Mixin
  • 通过向继承添加DynamicSerializerMixin初始化/调整序列化程序
  • 使用它:)
  • 正在运行的API

        @action(detail=True, serializer_class=YourSerialzierClass)
        def teams_roles(self, request, pk=None):
            user = self.get_object()
            queryset = user.roles.all()
            serializer = self.get_serializer(queryset, many=True, excluded_fields=['user'])
            return Response(data=serializer.data)
    

    序列化程序
    Charfield
    方法具有属性
    allow\u blank

    默认情况下,它设置为False。 将其设置为
    True
    将允许您在“序列化”期间将该字段标记为可选

    这是您应该编写的代码

    classification = serializers.CharField(source="Classification", allow_blank=True)
    

    注意:
    required
    属性用于反序列化。

    可以将这些字段序列化为
    None
    ,还是密钥根本不存在?它们根本不存在,我正在调用一个SOAP web服务,该服务具有使用SUD的可选字段,response对象表示返回的XML,在某些情况下不包括可选字段;理想情况下,我希望它们根本不存在,但是我暂时可以接受
    None
    。考虑到目前为止框架对我的项目是多么友好,最好为不存在的键提供一个开箱即用的选项,默认设置为
    None
    @TomChristie嘿,Tom,现在DRF 3不存在了,您是否可以使用DRF 3更新您的答案,以获得推荐的方法?我这样问是因为有几个人在建议使用DRF 3的不同方法,我不确定最好的选择是什么(见下面Mark的答案或此处David的答案:有关使用DRF 3实现这一点的方法示例,你会怎么做
    classification = serializers.CharField(source="Classification", allow_blank=True)