Python Django REST Framework - 序列化可选字段

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17552380/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 08:30:11  来源:igfitidea点击:

Django REST Framework - Serializing optional fields

pythondjangoserializationdjango-rest-framework

提问by Aziz Alfoudari

I have an object that has optional fields. I have defined my serializer this way:

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

class ProductSerializer(serializers.Serializer):
    code = serializers.Field(source="Code")
    classification = serializers.CharField(source="Classification", required=False)

I thoughtrequired=Falsewould do the job of bypassing the field if it doesn't exist. However, it is mentioned in the documentation that this affects deserialization rather than serialization.

如果它不存在,我认为required=False可以绕过该领域。但是,文档中提到这会影响反序列化而不是序列化。

I'm getting the following error:

我收到以下错误:

'Product' object has no attribute 'Classification'

Which is happening when I try to access .dataof the serialized instance. (Doesn't this mean it's deserialization that's raising this?)

当我尝试访问.data序列化实例时会发生这种情况。(这是否意味着反序列化引发了这个问题?)

This happens for instances that do not have Classification. If I omit Classificationfrom the serializer class it works just fine.

这发生在没有Classification. 如果我Classification从序列化器类中省略它就可以正常工作。

How do I correctly do this? Serialize an object with optional fields, that is.

我该如何正确地做到这一点?序列化具有可选字段的对象,即。

采纳答案by Tom Christie

The serializers are deliberately designed to use a fixed set of fields so you wouldn't easily be able to optionally drop out one of the keys.

序列化器被故意设计为使用一组固定的字段,因此您不能轻易地选择删除其中一个键。

You could use a SerializerMethodFieldto either return the field value or Noneif the field doesn't exist, or you could not use serializers at all and simply write a view that returns the response directly.

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

Update for REST framework 3.0serializer.fieldscan be modified on an instantiated serializer. When dynamic serializer classes are required I'd probably suggest altering the fields in a custom Serializer.__init__()method.

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

回答by Mark Chackerian

Django REST Framework 3.0+
Dynamic fields now supported, see http://www.django-rest-framework.org/api-guide/serializers/#dynamically-modifying-fields-- this approach defines all of the fields in the serializer, and then allows you to selectively remove the ones you don't want.

Django REST Framework 3.0+
现在支持动态字段,请参阅http://www.django-rest-framework.org/api-guide/serializers/#dynamically-modifying-fields- 这种方法定义了序列化程序中的所有字段,然后允许您有选择地删除您不想要的那些。

Or you could also do something like this for a Model Serializer, where you mess around with Meta.fields in the serializer 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)

You'd have to ask Tom though if this is the "correct way" since it may not fit in with the long term plan.

不过,您必须问汤姆这是否是“正确的方法”,因为它可能不符合长期计划。

Django REST Framework < 3.0
Try something like this:

Django REST Framework < 3.0
尝试这样的事情:

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

    def get_classification(self, obj):
        return getattr(obj, 'classification', None)

Multiple Serializers

多个序列化程序

Another approach would be to create multiple serializers with different sets of fields. One serializer inherits from another and adds additional fields. Then you can choose the appropriate serializer in the view with the get_serializer_classmethod. Here's an actual example of how I use this approach to call different serializers to present different user data if the user object is the same as the request user.

另一种方法是使用不同的字段集创建多个序列化程序。一个序列化器从另一个序列化器继承并添加额外的字段。然后您可以在视图中使用该get_serializer_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

Removing fields from representation

从表示中删除字段

Another approach that I've used in security contexts is to remove fields in the to_representationmethod. Define a method like

我在安全上下文中使用的另一种方法是删除to_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

and then in your serializer, call that method like

然后在您的序列化程序中,调用该方法,例如

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

This approach is straightforward and flexible, but it comes at the cost of serializing fields that are sometimes not displayed. But that's probably okay.

这种方法简单而灵活,但代价是序列化有时不显示的字段。但这可能没问题。

回答by ncoghlan

From the "it's a terrible hack relying on specific implementation details of both DRF and Django, but it works (at least for now)" files, here's the approach I used to include some additional debugging data in the response from a "create" method implementation on a serializer:

从“依赖于 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

This is a temporary approach that lets me use the browsable API to display some of the raw response data received from a particular remote service during the creation process. In the future, I'm inclined to keep this capability, but hide it behind a "report debugging info" flag in the creation request rather than returning the lower level info by default.

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

回答by Uri Shalit

For this purpose the serializers have the partialargument. If when the serializer is initialized you can pass partial=True. If you are using generics or mixins you can overrider the get_serializerfunction as follows:

为此,序列化程序有partial参数。如果在初始化序列化程序时您可以通过partial=True. 如果您使用的是泛型或 mixin,您可以get_serializer按如下方式覆盖该函数:

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

And that will do the trick.

这将解决问题。

Note:This allows all fields to be optional and not only a specific one. If you want only specifics, you can override the method (i.e. update) and add validations of existence for various fields.

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

回答by RAJ GUPTA

The method describe below did the work for me. Pretty simple,easy and worked for me.

下面描述的方法对我有用。非常简单,容易,对我有用。

DRF version used = djangorestframework (3.1.0)

使用的 DRF 版本 = djangorestframework (3.1.0)

class test(serializers.Serializer):
  id= serializers.IntegerField()
  name=serializers.CharField(required=False,default='some_default_value')

回答by pymen

DynamicSerializerfor DRF 3, which allows dynamicly specifying which fields will be used in serializer, which will be excluded, and optionally which will become required!

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

  1. Create Mixin
  1. 创建 Mixin
    class DynamicSerializerMixin:
        """
        A Serializer that takes an additional `fields` argument that
        controls which fields should be used.
        """

        def __init__(self, *args, **kwargs):
            # Don't pass the 'fields' arg up to the superclass
            fields = kwargs.pop("fields", None)
            excluded_fields = kwargs.pop("excluded_fields", None)
            required_fields = kwargs.pop("required_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)

                if isinstance(fields, dict):
                    for field, config in fields.items():
                        set_attrs(self.fields[field], config)

            if excluded_fields is not None:
                # Drop any fields that are not specified in the `fields` argument.
                for field_name in excluded_fields:
                    self.fields.pop(field_name)

            if required_fields is not None:
                for field_name in required_fields:
                    self.fields[field_name].required = True
  1. Initialize/adjust your serializerby adding DynamicSerializerMixin to inheritence
  1. 通过将 DynamicSerializerMixin 添加到继承来初始化/调整您的序列化程序

class UserProfileSerializer(DynamicSerializerMixin, serializers.ModelSerializer):

    class Meta:
        model = User
        fields = (
            "id",
            'first_name', 'last_name'
            "email",
            "is_staff",
        )
  1. Use it:)
  1. 使用它:)
class RoleInvitationSerializer(serializers.ModelSerializer):
    invited_by = UserProfileSerializer(fields=['id', 'first_name', 'last_name'])

or in action apis

在行动apis

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