39

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

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

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

我收到以下错误:

'Product' object has no attribute 'Classification'

当我尝试访问.data序列化实例时会发生这种情况。(这不是说是反序列化引起了这个吗?)

这发生在没有Classification. 如果我从序列化程序类中省略Classification它就可以了。

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

4

8 回答 8

27

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

或者你也可以为模型序列化器做这样的事情,你在序列化器初始化中弄乱了 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 Framework < 3.0
试试这样的:

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

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

多个序列化器

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

从表示中删除字段

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

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

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

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

于 2014-09-08T15:44:28.110 回答
20

下面描述的方法为我做了工作。非常简单,容易,为我工作。

使用的 DRF 版本 = djangorestframework (3.1.0)

class test(serializers.Serializer):
  id= serializers.IntegerField()
  name=serializers.CharField(required=False,default='some_default_value')
于 2018-02-23T07:35:41.583 回答
19

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

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

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

于 2013-07-10T08:14:45.763 回答
2

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

  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. 通过将 DynamicSerializerMixin 添加到继承来初始化/调整您的序列化程序

class UserProfileSerializer(DynamicSerializerMixin, serializers.ModelSerializer):

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

在行动 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)
于 2019-07-15T19:41:54.730 回答
1

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

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

这样就可以了。

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

于 2017-02-14T10:25:24.403 回答
1

serializersCharfield方法有一个属性allow_blank

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

这是您应该编写的代码

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

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

于 2021-06-11T11:50:35.220 回答
0

从“这是一个依赖于 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 来显示在创建过程中从特定远程服务接收到的一些原始响应数据。将来,我倾向于保留此功能,但将其隐藏在创建请求中的“报告调试信息”标志后面,而不是默认返回较低级别的信息。

于 2015-09-21T02:26:35.170 回答
0

对我来说行之有效的是像这样设置序列化程序:

classification = serializers.CharField(max_length=20, allow_blank=True, default=None)

于 2021-12-10T18:24:22.443 回答