我在 Django REST Framework view.py 中修改 response.data。为什么我在测试期间收到 AttributeError 而不是实际 API 使用?
I modify response.data in Django REST Framework view.py. Why am I getting an AttributeError during testing but not with actual API use?
我的 Django REST Framework API 工作正常。在 view.py 中,我修改了 OrderViewSet,def partial_update,在将 response.data 字典保存到数据库之前将另一个 key/value 对添加到它。当我用 Postman 调用 API 时,它可以正常工作。
但是,当我 运行 测试相同的功能时,它失败了并且 returns:
request.data["submission_date"] = datetime.now()
AttributeError: This QueryDict instance is immutable
如果在实际 API 使用期间未发生此错误,为什么我会在测试期间收到此错误?
View.py
class OrderViewSet(viewsets.ModelViewSet):
""" Includes custom PATCH functionality """
queryset = Order.objects.all().order_by('-order_date')
serializer_class = OrderSerializer
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = [permissions.IsAuthenticated]
def partial_update(self, request, *args, **kwargs):
""" Update status and timestamp fields accordingly """
status = request.data['status']
if status == 'submitted':
request.data["submission_date"] = datetime.now()
if status == 'packaged':
request.data["packaged_date"] = datetime.now()
if status in ['sold', 'canceled', 'abandoned']:
request.data["finished_date"] = datetime.now()
return super().partial_update(request, *args, **kwargs)
Test.py
def test_patch_order_status_from_cart_to_submitted(self):
""" test patching order status from cart to submitted """
order = sample_order(user=self.user)
payload = {
"status": "submitted"
}
res = self.client.patch(order_detail_url(order.id), payload)
self.assertEqual(res.status_code, status.HTTP_200_OK)
patched_order = Order.objects.get(id=order.id)
self.assertEqual(patched_order.status, 'submitted')
def test_submitted_timestamp(self):
""" test that patching order status to submitted also leaves timestamp """
order = sample_order(user=self.user)
payload = {
"status": "submitted"
}
self.client.patch(order_detail_url(order.id), payload)
patched_order = Order.objects.get(id=order.id)
self.assertNotEqual(patched_order.submission_date, None)
编辑:
如果我要像下面那样修改 request.data 的副本,我怎么能从函数中 return 它呢?
def partial_update(self, request, *args, **kwargs):
""" Update status and timestamp accordingly """
modified_data = request.data.copy()
status = request.data['status']
if status == 'submitted':
modified_data["submission_date"] = datetime.now()
if status == 'packaged':
modified_data["packaged_date"] = datetime.now()
if status in ['sold', 'canceled', 'abandoned']:
modified_data["finished_date"] = datetime.now()
return super().partial_update(request, *args, **kwargs)
解决方案:现在测试通过了。
views.py:
...
def partial_update(self, request, pk, *args, **kwargs):
""" Update status and timestamp accordingly """
order = Order.objects.get(id=pk)
serializer = OrderSerializer(order, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors)
Serializers.py:
class OrderSerializer(serializers.ModelSerializer):
def update(self, order, validated_data):
""" Update status and timestamp accordingly """
status = validated_data.get('status')
order.status = status
if status == 'submitted':
order.submission_date = datetime.now()
elif status == 'packaged':
order.packaged_date = datetime.now()
elif status in ['sold', 'canceled', 'abandoned']:
order.finished_date = datetime.now()
order.save()
return order
class Meta:
model = Order
fields = '__all__'
read_only_fields = ('id', 'user')
你是说修改request.data?我之前也遇到过这个错误。
然后我看了Django documentation QueryDict,我觉得“request.data”:
是dictionary-like,不是字典。
数据类型不可变。
修改前复制,如:
req_data = request.data.copy()
req_data['submission_date'] = datetime.now()
而在实际API使用过程中没有发生错误的原因,可能只是状态不在这些情况下。
另一方面,在这种情况下,我通常在models.py中设置一个modified_at或created_at DateTimeField,这样会记录submission_date每当修改或创建数据时自动。我觉得方便。
created_at = models.DateTimeField(auto_now_add=True, help_text="submission_date")
modified_at = models.DateTimeField(auto_now=True, help_text="re-submission_date")
编辑 使用 ModelSerializer 寻找另一种可能的解决方案:
目的:我们要更新的数据不只是直接基于request.data。
我们想使用一些其他的逻辑:if status == 'submitted' then...
我想 submission_date、packaged_date、finished_date 是订单模型的字段。
views.py
def partial_update(self, request, pk, *args, **kwargs):
order = Order.objects.get(id=pk)
serializer = OrderSerializer(order, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors)
serializers.py
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = '__all__'
def update(self, order, validated_data):
status = validated_data.get('status')
order.status = status
if status == 'submitted':
order.submission_date = datetime.now()
elif status == 'packaged':
order.packaged_date = datetime.now()
elif status in ['sold', 'canceled', 'abandoned']:
order.finished_date = datetime.now()
order.save()
return order
我的 Django REST Framework API 工作正常。在 view.py 中,我修改了 OrderViewSet,def partial_update,在将 response.data 字典保存到数据库之前将另一个 key/value 对添加到它。当我用 Postman 调用 API 时,它可以正常工作。
但是,当我 运行 测试相同的功能时,它失败了并且 returns:
request.data["submission_date"] = datetime.now()
AttributeError: This QueryDict instance is immutable
如果在实际 API 使用期间未发生此错误,为什么我会在测试期间收到此错误?
View.py
class OrderViewSet(viewsets.ModelViewSet):
""" Includes custom PATCH functionality """
queryset = Order.objects.all().order_by('-order_date')
serializer_class = OrderSerializer
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = [permissions.IsAuthenticated]
def partial_update(self, request, *args, **kwargs):
""" Update status and timestamp fields accordingly """
status = request.data['status']
if status == 'submitted':
request.data["submission_date"] = datetime.now()
if status == 'packaged':
request.data["packaged_date"] = datetime.now()
if status in ['sold', 'canceled', 'abandoned']:
request.data["finished_date"] = datetime.now()
return super().partial_update(request, *args, **kwargs)
Test.py
def test_patch_order_status_from_cart_to_submitted(self):
""" test patching order status from cart to submitted """
order = sample_order(user=self.user)
payload = {
"status": "submitted"
}
res = self.client.patch(order_detail_url(order.id), payload)
self.assertEqual(res.status_code, status.HTTP_200_OK)
patched_order = Order.objects.get(id=order.id)
self.assertEqual(patched_order.status, 'submitted')
def test_submitted_timestamp(self):
""" test that patching order status to submitted also leaves timestamp """
order = sample_order(user=self.user)
payload = {
"status": "submitted"
}
self.client.patch(order_detail_url(order.id), payload)
patched_order = Order.objects.get(id=order.id)
self.assertNotEqual(patched_order.submission_date, None)
编辑:
如果我要像下面那样修改 request.data 的副本,我怎么能从函数中 return 它呢?
def partial_update(self, request, *args, **kwargs):
""" Update status and timestamp accordingly """
modified_data = request.data.copy()
status = request.data['status']
if status == 'submitted':
modified_data["submission_date"] = datetime.now()
if status == 'packaged':
modified_data["packaged_date"] = datetime.now()
if status in ['sold', 'canceled', 'abandoned']:
modified_data["finished_date"] = datetime.now()
return super().partial_update(request, *args, **kwargs)
解决方案:现在测试通过了。
views.py:
...
def partial_update(self, request, pk, *args, **kwargs):
""" Update status and timestamp accordingly """
order = Order.objects.get(id=pk)
serializer = OrderSerializer(order, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors)
Serializers.py:
class OrderSerializer(serializers.ModelSerializer):
def update(self, order, validated_data):
""" Update status and timestamp accordingly """
status = validated_data.get('status')
order.status = status
if status == 'submitted':
order.submission_date = datetime.now()
elif status == 'packaged':
order.packaged_date = datetime.now()
elif status in ['sold', 'canceled', 'abandoned']:
order.finished_date = datetime.now()
order.save()
return order
class Meta:
model = Order
fields = '__all__'
read_only_fields = ('id', 'user')
你是说修改request.data?我之前也遇到过这个错误。
然后我看了Django documentation QueryDict,我觉得“request.data”:
是dictionary-like,不是字典。
数据类型不可变。
修改前复制,如:
req_data = request.data.copy() req_data['submission_date'] = datetime.now()
而在实际API使用过程中没有发生错误的原因,可能只是状态不在这些情况下。
另一方面,在这种情况下,我通常在models.py中设置一个modified_at或created_at DateTimeField,这样会记录submission_date每当修改或创建数据时自动。我觉得方便。
created_at = models.DateTimeField(auto_now_add=True, help_text="submission_date")
modified_at = models.DateTimeField(auto_now=True, help_text="re-submission_date")
编辑 使用 ModelSerializer 寻找另一种可能的解决方案:
目的:我们要更新的数据不只是直接基于request.data。 我们想使用一些其他的逻辑:if status == 'submitted' then...
我想 submission_date、packaged_date、finished_date 是订单模型的字段。
views.py
def partial_update(self, request, pk, *args, **kwargs):
order = Order.objects.get(id=pk)
serializer = OrderSerializer(order, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors)
serializers.py
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = '__all__'
def update(self, order, validated_data):
status = validated_data.get('status')
order.status = status
if status == 'submitted':
order.submission_date = datetime.now()
elif status == 'packaged':
order.packaged_date = datetime.now()
elif status in ['sold', 'canceled', 'abandoned']:
order.finished_date = datetime.now()
order.save()
return order