【问题标题】:Django List Serializer bulk update failing with 'QuerySet' object has no attribute 'pk'Django List Serializer 批量更新失败,'QuerySet' 对象没有属性'pk'
【发布时间】:2019-09-14 18:07:05
【问题描述】:

我正在尝试在Viewset 中使用ListSerializer 进行批量更新。我的批量创建工作正常,但不是 PUT。这是我的SerializerListSerizlier 和我的观点。

django=2.0.0

Serializer

class SampleListSerializer(serializers.ListSerializer):
    pass

class SampleSerializer(serializers.ModelSerializer):
    class Meta:
        list_serializer_class = SampleListSerializer
        model = Sample
        fields = ['id', 'name', 'last_name' ]

这是我的ViewSet


class SampleViewSet(viewsets.ModelViewSet):
    serializer_class = SampleSerializer
    queryset = Sample.objects.all()

    def get_serializer(self, *args, **kwargs):
        if "data" in kwargs:
            data = kwargs["data"]

            # check if many is required
            if isinstance(data, list):
                kwargs["many"] = True

        return super(SampleViewSet, self).get_serializer(*args, **kwargs)

    def put(self, request):
        sorted(request.data, key=lambda o: o["id"])
        instances = Sample.objects.filter(id__in=[o["id"] for o in request.data]).order_by("id")
        try:
            with transaction.atomic():
                ss = SampleSerializer(data=request.data, instance=instances, many=True)
                if ss.is_valid(raise_exception=True):
                    s = ss.save()
                    return Response(ss.data)
                return Response(status=status.HTTP_400_BAD_REQUEST)
        except Exception as e:
            print(f"In exception {e}")
            return Response(status=status.HTTP_400_BAD_REQUEST)

    def create(self, request, *args, **kwargs):
        return super().create(request, *args, **kwargs)

我在ss.is_valid() 处得到'QuerySet' 对象没有属性'pk'。有人可以建议我的错误在哪里吗?或者怎么了?

【问题讨论】:

  • 根据the docs,DRF 列表序列化程序不支持开箱即用的多个更新,但这些相同的文档描述了如何做到这一点。

标签: python django django-rest-framework django-views


【解决方案1】:

这里有一个改编的答案:https://github.com/miki725/django-rest-framework-bulk/issues/68

修复的最佳方法是在您的ListSerializer 上覆盖to_internal_value

def to_internal_value(self, data):
  """
  List of dicts of native values <- List of dicts of primitive datatypes.
  """
  if html.is_html_input(data):
    data = html.parse_html_list(data)

  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:
    if self.parent and self.partial:
      raise SkipField()

    message = self.error_messages['empty']
    raise ValidationError({
      api_settings.NON_FIELD_ERRORS_KEY: [message]
    }, code='empty')

  ret = []
  errors = []

  for item in data:
    try:
      # custom code
      self.child.instance = self.instance.get(id=item['id']) if self.instance else None
      self.child.initial_data = item
      validated = self.child.run_validation(item)
    except ValidationError as exc:
      errors.append(exc.detail)
    else:
      ret.append(validated)
      errors.append({})

  if any(errors):
    raise ValidationError(errors)

  return ret

【讨论】:

    【解决方案2】:

    instance 关键字采用单个 QuerySet 对象,但看起来您传递的是 "List QuerySet"(从技术上讲,它只是一个 QuerySet)。

    如果你 print(instances) 它看起来像这样(假设你没有覆盖类的 __str__ 方法):

    <QuerySet [<Sample: Sample object(1)>, <Sample: Sample object(2)>, ... ]>
    

    serializers.ModelSerializer 中的某处调用了instance.pk,这将始终导致:

    'QuerySet' 对象没有属性 'pk'

    【讨论】:

      猜你喜欢
      • 2021-06-18
      • 1970-01-01
      • 1970-01-01
      • 2020-02-11
      • 2020-10-31
      • 2023-03-11
      • 1970-01-01
      • 2013-02-02
      • 2021-08-05
      相关资源
      最近更新 更多