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

时间:2013-07-09 15:36:15

标签: python django serialization django-rest-framework

我有一个包含可选字段的对象。我用这种方式定义了我的序列化器:

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

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

我收到以下错误:

'Product' object has no attribute 'Classification'

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

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

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

7 个答案:

答案 0 :(得分:22)

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

或者你也可以为Model 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)

如果这是“正确的方式”,你必须问问汤姆。因为它可能不符合长期计划。

Django REST框架< 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

这种方法简单明了,但是以序列化有时不显示的字段为代价。但那可能还可以。

答案 1 :(得分:11)

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

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

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

答案 2 :(得分:3)

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

使用的DRF版本= djangorestframework(3.1.0)

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

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

答案 4 :(得分:0)

为此,序列化程序具有partial参数。如果初始化序列化程序,则可以传递partial=True。如果您使用的是泛型或mixins,则可以覆盖get_serializer函数,如下所示:

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

这就行了。

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

答案 5 :(得分:0)

DynamicSerializer (用于DRF 3),它允许动态指定在序列化程序中将使用哪些字段,哪些将被排除,以及可选地哪些是必填项!

  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)

答案 6 :(得分:0)

序列化器 Charfield 方法有一个属性 allow_blank

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

这是你应该写的代码

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

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