【问题标题】:Django REST framework create objects from list and return the errors to invalid objectsDjango REST框架从列表中创建对象并将错误返回给无效对象
【发布时间】:2014-03-13 14:33:20
【问题描述】:

我有以下代码

serializer.py

class ContactSerializer(serializers.ModelSerializer):

    def __init__(self, *args, **kwargs):
        many = kwargs.pop('many', True)
        many = True
        super(ContactSerializer, self).__init__(many=many, *args, **kwargs)

    class Meta:
        model = Contact

    def validate(self, attrs):
        # Check that the user in contact isn't the same as the parent user
        if 'user' in attrs.keys() and 'parent_user' in attrs.keys() and attrs['user']:
            if attrs['user'].pk == attrs['parent_user'].pk:
                raise serializers.ValidationError("You can't add yourself as a contact")
        return attrs

views.py

class ContactViewSet(viewsets.ModelViewSet):

    queryset = Contact.objects.all()
    serializer_class = ContactSerializer
    authentication_classes = (TokenAuthentication,)
    permission_classes = (permissions.IsAuthenticated, )

    def get_queryset(self):
        return Contact.objects.filter(parent_user=self.request.user.pk)

    def create(self, request):
        if isinstance(request.DATA, list):
            for i, contact_data in enumerate(request.DATA):
                # Get the user id from the request
                request.DATA[i]['parent_user'] = self.request.user.pk

                # Check if the contact is aleardy a user
                if 'email' in contact_data:
                    try:
                        request.DATA[i]['user'] = User.objects.get(email=contact_data['email']).pk
                    except User.DoesNotExist, e:
                        pass

        return super(ContactViewSet, self).create(request)

现在的问题是,当我在POST 中发送联系人列表时,没有创建任何对象,它只是发送无效对象的错误。例如: 发布

[
  {
    "first_name": "Eyad",
    "last_name": "tttttt",
    "email": "eyad@gmail.com"
  },
  {
    "first_name": "Eyad",
    "last_name": "mmmmmm",
    "email": "eyad2@gmail.com"
  }
]

回归

[
    {},
    {
        "non_field_errors": [
            "You can't add yourself as a contact"
        ]
    }
]

我怎样才能让这个返回像这样:

[
    {
        "created": true
    },
    {
        "non_field_errors": [
            "You can't add yourself as a contact"
        ]
    }
]

这样,当调用 API 时,会创建有效的对象,而 API 调用者不必再次发送这些对象。

【问题讨论】:

    标签: python django django-rest-framework


    【解决方案1】:

    我创建了CreateModelMixin 的子类来更改create 方法。

    class BulkCreateModelMixin(mixins.CreateModelMixin):
        """
        Create valid objects and return errors for invalid ones.
        """
    
        def create(self, request, *args, **kwargs):
            # The initial serializer
            serializer = self.get_serializer(data=request.DATA)
            return_list = []
    
            for item in zip(serializer.errors, serializer.init_data):
                # If item doesn't have errors
                if not item[0]:
    
                    # Create a an individual serializer for the valid object and save it
                    object_serializer = self.get_serializer(data=[item[1]])
                    if object_serializer.is_valid():
                        self.pre_save(object_serializer.object)
                        self.object = object_serializer.save(force_insert=True)
                        self.post_save(self.object, created=True)
    
                        return_list.append(object_serializer.data[0])
                else:
                    return_list.append(item[0])
    
            # Status code
            if serializer.errors:
                return_status = status.HTTP_206_PARTIAL_CONTENT
            else:
                return_status = status.HTTP_201_CREATED
    
            return Response(return_list, status=return_status)
    

    然后将新的 mixin 添加到视图集

    class ContactViewSet(viewsets.ModelViewSet, BulkCreateModelMixin):
        ..........
        ..........
    

    【讨论】:

    • 有谁知道这个问题的更好解决方案,现在是 2019 年?
    【解决方案2】:

    我用另一种方式解决了这个问题。 创建 2 个 mixin,一个用于视图(覆盖 create 方法,另一个用于 ListSerializer(覆盖 to_internal_value method)。

    所以我有:

    class CreateListMixin:
        """Allows bulk creation of a resource."""
    
        def get_serializer(self, *args, **kwargs):
            if isinstance(kwargs.get('data', {}), list):
                kwargs['many'] = True
    
            return super().get_serializer(*args, **kwargs)
    
        def create(self, request, *args, **kwargs):
            serializer = self.get_serializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            self.perform_create(serializer)
            headers = self.get_success_headers(serializer.data)
            return Response({"result": serializer.data, "errors": serializer.errors_list},
                            status=status.HTTP_202_ACCEPTED, headers=headers)
    
    
    class ErrorsListSerializerMixin:
        error_list = []
    
        def to_internal_value(self, data):
            """
            List of dicts of native values <- List of dicts of primitive datatypes.
            """
            if not isinstance(data, list):
                message = self.error_messages['not_a_list'].format(
                    input_type=type(data).__name__
                )
                raise ValidationError({
                    api_settings.NON_FIELD_ERRORS_KEY: [message]
                }, code='not_a_list')
            if not self.allow_empty and len(data) == 0:
                message = self.error_messages['empty']
                raise ValidationError({
                    api_settings.NON_FIELD_ERRORS_KEY: [message]
                }, code='empty')
    
            ret = []
            errors = []
    
            for item in data:
                try:
                    validated = self.child.run_validation(item)
                except ValidationError as exc:
                    errors.append({"error": exc.detail, "data": item})
                else:
                    ret.append(validated)
    
            if any(errors):
                self.errors_list = errors
                # raise ValidationError(errors)
            return ret
    

    views.py

    class SKUMatchesView(CreateListMixin, viewsets.ModelViewSet):
    ......................
    ......................
    

    serializers.py

    class SKUMatchesListSerializer(ErrorsListSerializerMixin, serializers.ListSerializer):
    .....................
    .....................
    

    所以,当我发布例如这些数据时:

    [{          "sku_customer_uid": "13864-20000000428265",
                "company": 1,
                "code": "123456784654654",
                "marketplace": "ozon"
    },
    {          "sku_customer_uid": "13864-2000000042824",
                "company": 1,
                "code": "123456789*8",
                "marketplace": "ozon"
    }
    ]
    

    我收到同样的回复:

    {
      "result": [
        {
          "id": 8821,
          "sku_customer_uid": null,
          "company": 1,
          "code": "123456784654654",
          "marketplace": "ozon"
        }
      ],
      "errors": [
        {
          "error": {
            "non_field_errors": [
              "Wrong code"
            ]
          },
          "data": {
            "sku_customer_uid": "13864-2000000042824",
            "company": 1,
            "code": "123456789*8",
            "marketplace": "ozon"
          }
        }
      ]
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-04-23
      • 2020-01-20
      • 2015-10-04
      • 2015-07-07
      • 1970-01-01
      • 2019-06-14
      • 1970-01-01
      相关资源
      最近更新 更多