【问题标题】:Django Rest Framework ModelSerializer custom serializer field to_internal_value doesn't save to objectDjango Rest Framework ModelSerializer 自定义序列化器字段 to_internal_value 不保存到对象
【发布时间】:2019-05-17 14:31:37
【问题描述】:

我有模型和序列化器,该模型中有 ArrayField(postgres)。

现在我想创建一个序列化器字段,它将接收列表 [1,2] 并将其保存到对象,但序列化器中的列表和详细信息显示 JSON 对象列表。

型号:

class User(models.Model):
    email = models.EmailField('Email', unique=True, blank=False)
    full_name = models.CharField(
        'Full name', max_length=150, blank=True, null=True)
    roles = ArrayField(
        models.PositiveSmallIntegerField(),
        default=list,
        blank=True
    )

序列化器:

class ArraySerializerField(ListField):

    def __init__(self, queryset, serializer_class):
        super(ArraySerializerField, self).__init__()
        self.queryset = queryset
        self.serializer_class = serializer_class

    def to_representation(self, value):
        if value:
            qs = self.queryset.filter(pk__in=value)
            return self.serializer_class(qs, many=True).data

        return []

    def to_internal_value(self, value):
        super(ArraySerializerField, self).to_internal_value(value)
        print(value)  # [1, 2]
        return value


class UserSerializer(SerializerExtensionsMixin, serializers.ModelSerializer):
    roles = ArraySerializerField(queryset=Role.objects.all(), serializer_class=RoleSerializer)

    class Meta:
        model = User
        fields = ('id', 'email', 'full_name', 'roles')

    def create(self, validated_data):
        print(validated_data) 
        # {'email': 'test@test.com', 'full_name': 'Test', 'roles': []}
        user = super(UserSerializer, self).create(validated_data)

        return user

现在,当我提出列表或详细信息请求时,一切正常,我得到一个 JSON 形式的角色列表。

但是当我尝试发布数据并使用此数据发送时:

{
  "email": "test@test.com",
  "full_name": "Test",
  "roles": [1, 2]
}

create 方法中的validated_data 始终将角色显示为[],并且在没有角色的情况下保存对象,但从to_internal_value 打印显示[1, 2]

我做错了什么?它应该保存发送的数据,因为to_internal_value 工作正常。

编辑:

GET 和 LIST 响应给了我正确的格式:

{
  "id": 1,
  "email": "test@test.com",
  "full_name": "Test",
  "roles": [
    {
      "id": 1,
      "name": "Role 1"
    },
    {
      "id": 2,
      "name": "Role 2"
    }
  ]
}

【问题讨论】:

    标签: python django postgresql django-rest-framework


    【解决方案1】:

    你试过了吗?

    class UserSerializer(SerializerExtensionsMixin, serializers.ModelSerializer):
        roles = serializers.ListField(child=serializers.IntegerField(), allow_empty=True, required=False)
    
        class Meta:
            model = User
            fields = ('id', 'email', 'full_name', 'roles')
    
        def create(self, validated_data):
            # check validated_data here
            ...

    注意

    我不确定 SerializerExtensionsMixin 类的性质。另外我不确定您的自定义 ListField

    querysetserializer_class 参数背后的意图

    Django Shell 输出

    In [7]: from rest_framework import serializers                                                                                                                                                                     
    
    In [8]: class UserSerializer(serializers.Serializer):  # Created a simple serializer without model
       ...:     roles = serializers.ListField(child=serializers.IntegerField(), allow_empty=True, required=False) 
       ...:     email = serializers.EmailField() 
       ...:     full_name = serializers.CharField() 
       ...:                                                                                                                                                                                                            
    
    In [9]: data = { # your data
       ...:   "email": "test@test.com", 
       ...:   "full_name": "Test", 
       ...:   "roles": [1, 2] 
       ...: }                                                                                                                                                                                                          
    
    In [10]: u = UserSerializer(data=data)                                                                                                                                                                             
    
    In [11]: u.is_valid()                                                                                                                                                                                              
    Out[11]: True
    
    In [12]: u.data  # got valid data                                                                                                                                                                                                  
    Out[12]: {'roles': [1, 2], 'email': 'test@test.com', 'full_name': 'Test'}
    
    In [13]: data["roles"] = [] # change data to accept empty list                                                                                                                                                                                       
    
    In [14]: u = UserSerializer(data=data)                                                                                                                                                                             
    
    In [15]: u.is_valid()                                                                                                                                                                                              
    Out[15]: True
    
    In [16]: u.data  # got validated data with empty list                                                                                                                                                                                                  
    Out[16]: {'roles': [], 'email': 'test@test.com', 'full_name': 'Test'} 
    
    In [17]: data["roles"] = ["foo","bar"]   #added string to the list                                                                                                                                                                          
    
    In [18]: u = UserSerializer(data=data)                                                                                                                                                                             
    
    In [19]: u.is_valid()  # validation failed                                                                                                                                                                                            
    Out[19]: False
    
    In [20]: u.errors                                                                                                                                                                                                  
    Out[20]: {'roles': {0: [ErrorDetail(string='A valid integer is required.', code='invalid')], 1: [ErrorDetail(string='A valid integer is required.', code='invalid')]}}

    更新-1

    创建一个 RoleSerializer 并在 UserSerializer

    中使用它
    class RoleSerializer(serializers.ModelSerializer):
        class Meta:
            model = Role
            fields = ('id', 'name')
    
    
    class UserSerializer(SerializerExtensionsMixin, serializers.ModelSerializer):
        roles = serializers.ListField(child=serializers.IntegerField(), allow_empty=True, required=False)
    
        class Meta:
            model = User
            fields = ('id', 'email', 'full_name', 'roles')
    
        def create(self, validated_data):
            # check validated_data here
            ...
    
        def to_representation(self, instance):
            rep = super().to_representation(instance)
            rep['roles'] = RoleSerializer(Role.objects.filter(id__in=rep['roles']), many=True).data
            return rep

    更新-2

    使用自定义数组字段

    class ArrayField(serializers.ListField):
        def __init__(self, *args, **kwargs):
            self.queryset = kwargs.pop('queryset', None)
            self.serializer_class = kwargs.pop('serializer_class', None)
            super().__init__(*args, **kwargs)
    
        def to_representation(self, data):
            qs = self.queryset.filter(id__in=data)
            serializer = self.serializer_class(qs,many=True)
            return serializer.data
    
    class UserSerializer(SerializerExtensionsMixin, serializers.ModelSerializer):
        roles = ArrayField(queryset=Role.objects.all(), serializer_class=RoleSerializer)
    
        class Meta:
            model = User
            fields = ('id', 'email', 'full_name', 'roles')
    
        def create(self, validated_data):
            # check validated_data here
            ...

    【讨论】:

    • 这将帮助我接收 [1,2] 并保存它,但正如你所看到的,在 u.data(第 12 行)你得到 [1,2] 作为角色,但我想获取对象列表例如[{"id":1, "name":'Role 1"},{"id":2, "name":'Role 2"}],因为我在我的自定义字段中使用querysetserializer_class。所以这不像我预期的那样工作。
    • create方法中需要哪种类型的数据?
    • 整数的Attar。我在内部值中得到它,但在 valid_data 中它是空的。
    • 在我的解决方案中,保存过程是可以的……你需要更改响应……对吗?
    • 它有效,谢谢。 to_internal_value 已被删除并且它可以工作,这很奇怪。再次感谢。
    【解决方案2】:

    尝试切换到PrimaryKeyRelatedField。尽管您需要更改用户模型以使用实际关系。这通常是一个好主意,因为它有助于加强项目的数据完整性。

    class User(models.Model):
        email = models.EmailField('Email', unique=True, blank=False)
        full_name = models.CharField(
            'Full name', max_length=150, blank=True, null=True)
        roles = models.ManyToManyField(Role, blank=True)
    
    class UserSerializer(SerializerExtensionsMixin, serializers.ModelSerializer):
        roles = serializers.PrimaryKeyRelatedField(
            many=True,
            queryset=Role.objects.all(),
        )
    
        class Meta:
            model = User
            fields = ('id', 'email', 'full_name', 'roles')
    
        def create(self, validated_data):
            print(validated_data) 
            # {'email': 'test@test.com', 'full_name': 'Test', 'roles': []}
            user = super(UserSerializer, self).create(validated_data)
    
            return user
    

    【讨论】:

    • 我知道,但我不想再使用一个数据库表,这就是我使用 ArrayField 的原因。我在 ArrayField 上需要这个解决方案。
    • 你真的应该。这是一个更好的解决方案。
    猜你喜欢
    • 1970-01-01
    • 2015-03-12
    • 2014-11-09
    • 2017-03-10
    • 1970-01-01
    • 2013-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多