我在django rest框架view.py中修改响应。为什么我在测试过程中获得属性,而不是实际使用API​​?

发布于 2025-01-22 14:27:49 字数 3868 浏览 0 评论 0原文

我的Django REST框架API正常工作。在view.py中,我修改订单视图集(def partial_update),将另一个键/值对添加到响应。数据将其保存到db之前。当我与Postman一起致电API时,它无济于事。

但是,当我运行相同功能的测试时,它会失败并返回:

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的副本,如下所示,我该如何从函数中返回?

    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:serializers.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)

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

My Django REST Framework API is working just as expected. In view.py, I modify OrderViewSet, def partial_update, to add another key/value pair to the response.data dict before it is saved to the db. It works without errors when I call the API with Postman.

However, when I run tests for the same functionality it fails and returns:

request.data["submission_date"] = datetime.now()
AttributeError: This QueryDict instance is immutable

Why would I get this error during testing if it is not occurring during actual API use?

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)

Edit:

If I were to modify a copy of request.data as below, how could I then return it from the function?

    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)

Solution: Now the tests pass.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

梦境 2025-01-29 14:27:49

您的意思是修改request.data?我以前也有这个错误。

然后我阅读 django文档“ request.data”:

  • 是字典,而不是字典。

  • 数据类型是不可变的。

  • 在修改之前复制它,例如:

      req_data = request.data.copy()
     req_data ['cubmission_date'] = dateTime.now()
     

以及在实际API使用过程中未发生错误的原因,也许只是在这些条件下的状态。

另一方面,在这种情况下,我通常会设置Modified_at或create_at “ noreflow noreferrer”> DateTimeField 在型号。我觉得这很方便。

created_at = models.DateTimeField(auto_now_add=True, help_text="submission_date")
modified_at = models.DateTimeField(auto_now=True, help_text="re-submission_date")

编辑使用Modelialializer:

目的:我们不仅要直接基于request.data更新数据。
我们想使用其他一些逻辑:如果status =='提交'然后...

我想提交_date,packaged_date,dearter_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

You mean modifying request.data? I also got this error before.

Then I read Django documentation QueryDict, I think the "request.data":

  • is dictionary-like, not dictionary.

  • data type is immutable.

  • copy that before modifying, like:

     req_data = request.data.copy()
     req_data['submission_date'] = datetime.now()
    

And the reason of error not occurring during actual API use, maybe just the status not in these conditions.

On the other hand, in this case I usually set a modified_at or created_at DateTimeField in models.py, so that will record the submission_date automatically whenever the data is modified or created. I feel that is convenient.

created_at = models.DateTimeField(auto_now_add=True, help_text="submission_date")
modified_at = models.DateTimeField(auto_now=True, help_text="re-submission_date")

Edit for another possible solution by using ModelSerializer:

Purpose: We want to update the data not just based on request.data directly.
We want to use some other logic: if status == 'submitted' then...

And I suppose submission_date, packaged_date, finished_date are fields of Order Model.

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文