如何在Django Rest Framework中为具有特定条件的对象列表创建删除端点?

huangapple go评论57阅读模式
英文:

How to make a Delete endpoint for a list of objects with a given condition in Django Rest Framework?

问题

对于使用Django Rest Framework开发的REST API,如果您想要创建一个基于删除请求中发送的条件来删除多个对象的端点,以下是一个合适的解决方案:

您可以使用HTTP DELETE请求来删除多个对象,但是通常来说,HTTP DELETE请求不应该包含请求体(request body)。相反,您可以将条件信息放在URL中或使用查询参数来指定删除的条件。以下是一个示例实现:

首先,您可以在urls.py中创建一个带有条件参数的URL:

from django.urls import path
from . import views

urlpatterns = [
    # ... 其他URL配置 ...
    path('delete-products/', views.DeleteProducts.as_view(), name='delete-products'),
]

然后,您可以创建一个视图类 DeleteProducts 来处理删除请求,并使用查询参数来指定要删除的条件。这里使用Django Rest Framework的DestroyAPIView来实现删除:

from rest_framework.generics import DestroyAPIView
from rest_framework import status
from rest_framework.response import Response
from .models import Product
from .serializers import ProductSerializer

class DeleteProducts(DestroyAPIView):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

    def destroy(self, request, *args, **kwargs):
        brand_to_delete = request.query_params.get('brand_to_delete')
        if brand_to_delete:
            products_to_delete = self.queryset.filter(brand=brand_to_delete)
            if products_to_delete.exists():
                products_to_delete.delete()
                return Response(status=status.HTTP_204_NO_CONTENT)
            else:
                return Response({'detail': 'No products found for the specified brand.'}, status=status.HTTP_404_NOT_FOUND)
        else:
            return Response({'detail': 'Brand to delete not specified in query parameters.'}, status=status.HTTP_400_BAD_REQUEST)

这种方法将条件作为查询参数传递给API端点,这是一个更常见的做法,并且不需要将删除请求的条件放在请求体中。这样做的好处是可以更容易地测试和使用标准HTTP DELETE请求来执行删除操作。

然后,您可以使用类似以下的Python脚本来发送删除请求:

import requests

delete_url = 'your_api_base_url/delete-products/?brand_to_delete=X'
response = requests.delete(delete_url)

if response.status_code == 204:
    print('Deletion successful')
else:
    print('Deletion failed')

请注意,这里将brand_to_delete作为查询参数传递给URL,这是一种通常的做法,而不是将其包含在请求体中。

这是一种更常见的做法,遵循了HTTP DELETE请求的标准,并且在URL配置上更加清晰和直观。

英文:

Given an REST API developed with Django Rest Framework, what would be the proper solution for creating an endpoint that deletes multiple objects , based on a criteria that is send along with the Delete request?

I have a simple model for products. One of the model-fields is the brand. Now I would like to let another python script send a delete request to the API and remove all products that are from brand X.

The answer from this other post is exactly what I want. However, this it seems that the current version of the python request package does not allow a data-body to be sent along with a delete request.

I currently solved it my changing the endpoint in a POST request and overriding the functions as shown below:

class DeleteProductList(APIView):
    queryset = Product.objects.all()
    serializer_class = productserializer
    permission_classes = [IsAdminUser]

    def post(self, request, format=None):
        brand_to_delete = request.data.get('brand_to_delete')
        delete_products = self.queryset.filter(brand=brand_to_delete)
        delete_products.delete()
        return Response( self.serializer_class(delete_products,many=True).data) 

Now any python script can send a Post request to this endpoint to delete the products with brand X:

requests.post(url=delete_url, headers=headers, data={"brand_to_delete":'X'})

This works fine, but it seems not the best practise: A POST request for a deletion is used. In addition, the urls.py will look ugly and not intuitive. The modelviewset (ProductViewSet) is nicely handled by the router, but an additional endpoint is needed for deleting the objects.

from products.api import views as bv

router = DefaultRouter()

router.register(r"products", bv.ProductdViewSet)

urlpatterns = [
    path("", include(router.urls)),
    path("delete-products/", bv.DeleteProductList.as_view())
]

What would be the best practise for this?

答案1

得分: 0

如此解释,你可以通过查询参数传递数据并将代码更改如下:

from rest_framework.decorators import action
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.request import Request

# 导入你的模型和序列化器类

class AlbumViewSet(viewsets.ModelViewSet):
    queryset = Album.objects.all()
    serializer_class = AlbumSerializer

    @action(methods=["DELETE"], details=False)
    def delete(self, request: Request):
        brand = request.GET["brand_to_delete"]
        album_qs = Album.objects.filter(brand=brand_to_delete)
        album_qs.delete()
        return Response(self.serializer_class(album_qs, many=True).data)

以及调用部分:

requests.delete(url=f"{delete_url}?brand_to_delete=X", headers=headers)
英文:

As explained in this post you can solve the problem passing data via query parameters and changing your code like this:

from rest_framework.decorators import action
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.request import Request


# import your model and serializer classes

class AlbumViewSet(viewsets.ModelViewSet):
    queryset = Album.objects.all()
    serializer_class = AlbumSerializer

    @action(methods=["DELETE"], details=False, )
    def delete(self, request: Request):
        brand = request.GET["brand_to_delete"]
        album_qs = Album.objects.filter(brand=brand_to_delete)
        album_qs.delete()
        return Response(self.serializer_class(album_qs, many=True).data)

and the call:

requests.delete(url=f"{delete_url}?brand_to_delete=X", headers=headers)

huangapple
  • 本文由 发表于 2023年7月11日 05:50:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/76657547.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定