gpt4 book ai didi

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

转载 作者:太空狗 更新时间:2023-10-29 18:08:04 36 4
gpt4 key购买 nike

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

Getting KeyError when attempting to get a value for field <field> on serializer <serializer>.

这发生在所有字段上(因为事实证明我正在尝试访问父而不是子上的 .data,见下文)

类定义如下所示:

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',
...,
)

CustomProductListSerializerserializers.ListSerializer并且有一个被覆盖的 save()允许它正确处理批量创建和更新的方法。

这是批量产品 ViewSet 的示例 View :

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在跟踪处(或之后的行,显然)会导致错误。这是完整的跟踪(tl;dr 跳过下面我用调试器诊断的地方):

 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'."

在回溯的 L657(source here) 我有:

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

这让我想知道(在跟踪中进一步挖掘)为什么 serializer.fields 不可用。我怀疑这是因为序列化程序是 CustomProductListSerializer parent ,而不是 BulkProductSerializer child ,我是对的。在返回 Response(serializer.data) 之前的 pdb 跟踪中:

(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)}

好的,那么获得完整 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

最佳答案

如评论中所述,我仍然认为异常可能是因为 BulkProductSerializer 类中的用户字段,与 ListSerializer

没有任何关系

序列化程序 DRF 中可能存在另一个小错误(但很重要),如文档中所述 here .以下是指定 list_serializer_class 的方法:

class CustomListSerializer(serializers.ListSerializer):
...

class CustomSerializer(serializers.Serializer):
...
class Meta:
list_serializer_class = CustomListSerializer

请注意,它是在 Meta 类内部指定的,而不是在外部指定的。所以我认为在您的代码中,使用 many=True 切换到 List Serializer 将无法理解。那应该会导致不更新的问题。

更新 - 添加更新嵌套列表序列化程序的示例

问题似乎更多地是关于实现嵌套列表序列化器更新的通用方法,而不是实际错误。因此,我将尝试提供示例代码。

一些注意事项:

  • 如果我们使用ModelViewSet,列表路由将不允许PUTPATCH,所以更新 partial_update 也不会被调用 ( reference )。所以我直接使用POST,这样就简单多了。
  • 如果您想使用PUT/PATCH,请查看此答案here
  • 我们总是可以直接向 Post 请求添加一个查询参数,例如 allow_updatepartial 以区分 POST/PUT/PATCH
  • 我不会像问题那样使用uuid,而是使用普通的id,它应该非常相同

比较简单

供引用,模型如下所示:

class Product(models.Model):
name = models.CharField(max_length=200)
user = models.ForeignKey(User, null=True, blank=True)

def __unicode__(self):
return self.name

第 1 步:确保序列化程序更改为 ListSerializer

class ProductViewSet(viewsets.ModelViewSet):
serializer_class = ProductSerializer
queryset = Product.objects.all()

def get_serializer(self, *args, **kwargs):
# checking for post only so that 'get' won't be affected
if self.request.method.lower() == 'post':
data = kwargs.get('data')
kwargs['many'] = isinstance(data, list)
return super(ProductViewSet, self).get_serializer(*args, **kwargs)

第 2 步:通过重写 create 函数实现 ListSerializer

class ProductListSerializer(serializers.ListSerializer):
def create(self, validated_data):
new_products = [Product(**p) for p in validated_data if not p.get('id')]
updating_data = {p.get('id'): p for p in validated_data if p.get('id')}
# query old products
old_products = Product.objects.filter(id__in=updating_data.keys())
with transaction.atomic():
# create new products
all_products = Product.objects.bulk_create(new_products)
# update old products
for p in old_products:
data = updating_data.get(p.id, {})
# pop id to remove
data.pop('id')
updated_p = Product(id=p.id, **data)
updated_p.save()
all_products.append(updated_p)
return all_products


class ProductSerializer(serializers.ModelSerializer):
user = serializers.SlugRelatedField(slug_field='username', queryset=User.objects.all())
id = serializers.IntegerField(required=False)

class Meta:
model = Product
fields = '__all__'
list_serializer_class = ProductListSerializer

关于python - 如何在 DRF 中访问 ListSerializer 父类的 serializer.data?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45532405/

36 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com