如何在DRF中的ListSerializer父类上访问serializer.data?

时间:2017-08-06 13:16:37

标签: python django django-rest-framework

我在serializer.data中返回Response(serializer.data, status=something)之前尝试访问<field>时收到错误:

  

尝试在序列化程序<serializer>上获取字段.data的值时获取KeyError。

这种情况发生在所有字段上(因为事实证明我试图访问父母而不是孩子的class BulkProductSerializer(serializers.ModelSerializer): list_serializer_class = CustomProductListSerializer user = serializers.CharField(source='fk_user.username', read_only=False) class Meta: model = Product fields = ( 'user', 'uuid', 'product_code', ..., ) ,见下文)

类定义如下所示:

CustomProductListSerializer

serializers.ListSerializersave(),并且有一个重写的ViewSet方法,可以正确处理批量创建和更新。

以下是批量产品def partial_update(self, request): serializer = self.get_serializer(data=request.data, many=isinstance(request.data, list), partial=True) if not serializer.is_valid(): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) serializer.save() pdb.set_trace() return Response(serializer.data, status=status.HTTP_200_OK) 的示例视图:

serializer.data

尝试访问跟踪中的 Traceback (most recent call last): File "/lib/python3.5/site-packages/django/core/handlers/exception.py", line 41, in inner response = get_response(request) File "/lib/python3.5/site-packages/django/core/handlers/base.py", line 249, in _legacy_get_response response = self._get_response(request) File "/lib/python3.5/site-packages/django/core/handlers/base.py", line 187, in _get_response response = self.process_exception_by_middleware(e, request) File "/lib/python3.5/site-packages/django/core/handlers/base.py", line 185, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "/lib/python3.5/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view return view_func(*args, **kwargs) File "/lib/python3.5/site-packages/rest_framework/viewsets.py", line 86, in view return self.dispatch(request, *args, **kwargs) File "/lib/python3.5/site-packages/rest_framework/views.py", line 489, in dispatch response = self.handle_exception(exc) File "/lib/python3.5/site-packages/rest_framework/views.py", line 449, in handle_exception self.raise_uncaught_exception(exc) File "/lib/python3.5/site-packages/rest_framework/views.py", line 486, in dispatch response = handler(request, *args, **kwargs) File "/application/siop/views/API/product.py", line 184, in partial_update return Response(serializer.data, status=status.HTTP_200_OK) File "/lib/python3.5/site-packages/rest_framework/serializers.py", line 739, in data ret = super(ListSerializer, self).data File "/lib/python3.5/site-packages/rest_framework/serializers.py", line 265, in data self._data = self.to_representation(self.validated_data) File "/lib/python3.5/site-packages/rest_framework/serializers.py", line 657, in to_representation self.child.to_representation(item) for item in iterable File "/lib/python3.5/site-packages/rest_framework/serializers.py", line 657, in <listcomp> self.child.to_representation(item) for item in iterable File "/lib/python3.5/site-packages/rest_framework/serializers.py", line 488, in to_representation attribute = field.get_attribute(instance) File "/lib/python3.5/site-packages/rest_framework/fields.py", line 464, in get_attribute raise type(exc)(msg) KeyError: "Got KeyError when attempting to get a value for field `user` on serializer `BulkProductSerializer`.\nThe serializer field might be named incorrectly and not match any attribute or key on the `OrderedDict` instance.\nOriginal exception text was: 'fk_user'." (或之后的行,显然)会导致错误。这里有完整的跟踪(tl; dr跳过我用调试器诊断的地方):

iterable = data.all() if isinstance(data, models.Manager) else data
return [
    self.child.to_representation(item) for item in iterable
]

在追溯的L657(source here)我得到了:

CustomProductListSerializer

这让我想知道(在追踪中进一步挖掘)为什么serializer.fields不可用。我怀疑是因为序列化程序是BulkProductSerializer父级,而不是Response(serializer.data)子级,我是对的。在返回(Pdb) serializer.fields *** AttributeError: 'CustomProductListSerializer' object has no attribute 'fields' (Pdb) serializer.child.fields {'uuid': UUIDField(read_only=False, required=False, validators=[]) ...(etc)} (Pdb) 'user' in serializer.child.fields True (Pdb) serializer.data *** KeyError: "Got KeyError when attempting to get a value for field `user` on serializer `BulkProductSerializer`.\nThe serializer field might be named incorrectly and not match any attribute or key on the `OrderedDict` instance.\nOriginal exception text was: 'fk_user'." (Pdb) serializer.child.data {'uuid': '08ec13c0-ab6c-45d4-89ab-400019874c63', ...(etc)}

之前的pdb跟踪中
serializer.data

好的,那么在partial_update ViewSet所描述的情况下,获取完整class CustomProductListSerializer(serializers.ListSerializer): def save(self): instances = [] result = [] pdb.set_trace() for obj in self.validated_data: uuid = obj.get('uuid', None) if uuid: instance = get_object_or_404(Product, uuid=uuid) # Specify which fields to update, otherwise save() tries to SQL SET all fields. # Gotcha: remove the primary key, because update_fields will throw exception. # see https://stackoverflow.com/a/45494046 update_fields = [k for k,v in obj.items() if k != 'uuid'] for k, v in obj.items(): if k != 'uuid': setattr(instance, k, v) instance.save(update_fields=update_fields) result.append(instance) else: instances.append(Product(**obj)) if len(instances) > 0: Product.objects.bulk_create(instances) result += instances return result 并在父序列化程序类的resopnse中返回它的正确方法是什么? }?

编辑:

[out, string]

3 个答案:

答案 0 :(得分:0)

在我尝试访问serializer.data并获取KeyError的跟踪点,我注意到serializer.data仅包含initial_data中的键/值对,而不是实例数据(因此,我想,KeyError;某些模型字段的键不存在,因为它是partial_update请求)。但是,serializer.child.data确实包含列表中最后一个子项的所有实例数据。

所以,我转到定义data的{​​{3}}:

249    @property
250    def data(self):
251        if hasattr(self, 'initial_data') and not hasattr(self, '_validated_data'):
252            msg = (
253                'When a serializer is passed a `data` keyword argument you '
254                'must call `.is_valid()` before attempting to access the '
255                'serialized `.data` representation.\n'
256                'You should either call `.is_valid()` first, '
257                'or access `.initial_data` instead.'
258            )
259            raise AssertionError(msg)
260
261        if not hasattr(self, '_data'):
262            if self.instance is not None and not getattr(self, '_errors', None):
263                self._data = self.to_representation(self.instance)
264            elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None):
265                self._data = self.to_representation(self.validated_data)
266            else:
267                self._data = self.get_initial()
268        return self._data

第265行存在问题。我可以通过在断点处调用serializer.child.to_representation({'uuid': '87956604-fbcb-4244-bda3-9e39075d510a', 'product_code': 'foobar'})来复制错误。

调用partial_update()可以在单个实例上正常工作(因为设置了self.instanceself.to_representation(self.instance)正常工作)。但是,对于批量partial_update()实现,self.validated_data缺少模型字段,而to_representation()将无效,因此我将无法访问.data属性。

一种选择是维护某种self.instances Product实例列表,并在第265行覆盖data的定义:

self._data = self.to_representation(self.instances)

我真的更喜欢那些在这类问题上更有经验的人给出的答案,因为我不确定这是否是一个明智的解决方案,因此我会放弃赏金,希望有人可以提出更聪明的建议要做。

答案 1 :(得分:0)

您的错误与ListSerializer无关,但在获取字段user时出现问题:

  

KeyError:“在序列化程序user上尝试获取字段BulkProductSerializer的值时出现KeyError。

     

序列化程序字段可能名称不正确,与OrderedDict实例上的任何属性或键都不匹配。

     

原始例外文字是:'fk_user'。“

确保您的Product模型有fk_user字段。

您还在user上将BulkProductSerializer字段定义为可写,但尚未告诉序列化程序如何处理它...

更正此问题的最简单方法是使用SlugRelatedField

class BulkProductSerializer(serializers.ModelSerializer):

    list_serializer_class = CustomProductListSerializer

    user = serializers.SlugRelatedField(
                            slug_field='username',
                            queryset=UserModel.objects.all(),
                            source='fk_user'
    )

    class Meta:
        model = Product
        fields = (
            'user',
            'uuid',
            'product_code',
            ...,
        )

这应该处理很好的错误,例如当username不存在时......

答案 2 :(得分:-1)

如果您使用Django auth模型并设置read_only = True,请删除源。

user = serializers.CharField(read_only = True)

希望这适合你