django 覆盖ModelViewSet中def destroy()的默认行为以允许查询参数

ioekq8ef  于 2023-03-04  发布在  Go
关注(0)|答案(1)|浏览(137)

我理解DRF ModelViewSet中预期的DELETE行为是必须指定pk。例如,DELETE /api/document/1/
但是,我尝试使用REST API中可以接受的查询参数来实现DELETE,例如DELETE /api/document/?product_id=1,它将DELETEproduct_id关联的所有文档。
当你使用DELETE将查询参数传递给Django时,它返回:

{
    "detail": "Method \"DELETE\" not allowed."
}

删除这些参数并指定pk,它可以正常工作。
在这种情况下,如果您想删除与product_id相关的所有文档,首先需要查询以获得与该product_id相关的documents列表,在每个pk上迭代该列表和DELETE
似乎应该有更好的办法。
我正在测试是否可以覆盖destroy(),获取查询参数,并执行需要执行的操作:

class DocumentToProductViewSet(viewsets.ModelViewSet):
    queryset = DocumentToProduct.objects.all()
    serializer_class = DocumentToProductSerializer

    def destroy(self, request):
        print('destroy test')
        pass

由于'destroy test'没有在控制台中打印出来,因此在我收到该错误之前,请求没有到达该方法。
我使用的SimpleRouter()很可能是产生错误的地方,我也尝试了以下方法,但我不想添加另一个端点,如/api/documents/delete/?product_id=1

@action(method=['delete'], detail=False)
def delete(self, request):
    ...

只是想做一个:DELETE /api/documents/?product_id=1 .
在Django / DRF中有没有办法做到这一点,以允许查询参数通过DELETE上的路由器?

eeq64g8w

eeq64g8w1#

你是对的,这里的问题是你使用的是一个内置路由器,这正是SimpleRouter构建URL的方式,它需要一个lookup参数用于destroy操作(或者删除请求,根据你的意愿):

...
Route(
    url=r'^{prefix}/{lookup}{trailing_slash}$',
    mapping={
        'get': 'retrieve',
        'put': 'update',
        'patch': 'partial_update',
        'delete': 'destroy'
    },
    name='{basename}-detail',
    detail=True,
    initkwargs={'suffix': 'Instance'}
),
...

例如,您可以使用自定义路由器将此'delete': 'destroy'Map移动到另一个URL。在这种情况下,您希望将DELETE请求发送到api/documents/,它也是您的list端点,因此:

from rest_framework.routers import Route, DynamicRoute, SimpleRouter

class CustomRouter(SimpleRouter):
    routes = [
        Route(
            url=r'^{prefix}/$',
            mapping={'get': 'list', 'delete': 'destroy'},
            name='{basename}-list',
            detail=False,
            initkwargs={'suffix': 'List'}
        ),
        Route(
            url=r'^{prefix}/{lookup}/$',
            mapping={'get': 'retrieve'},
            name='{basename}-detail',
            detail=True,
            initkwargs={'suffix': 'Detail'}
        )
    ]

custom_router = CustomRouter()
custom_router.register(r'documents', DocumentToProductViewSet)

然后,处理destroy操作:

class DocumentToProductViewSet(viewsets.ModelViewSet):
    queryset = DocumentToProduct.objects.all()
    serializer_class = DocumentToProductSerializer

    def destroy(self, request, *args, **kwargs):
        product_id = request.GET.get('product_id', None)
        if product_id:
            try:
                product = Product.objects.get(id=product_id)
                product.documents.all().delete()

            except DocumentToProduct.DoesNotExist:
                return Response({'message': 'object not found'})
        else:
            return Response({'message': 'query string parameter not found'})

        return Response({'message': 'object documents successfully deleted.'})

相关问题