Django中PUT请求处理后重定向,能否转为GET请求获取Book数据?
我的Django项目中存在核心模型Book及其关联的BookViewSet(基于Django REST Framework)。前端编辑Page对象时,会向对应的PageViewSet发送请求。由于修改Page会影响关联的Book,我希望在处理完Page的PUT请求后,返回更新后的BookViewSet数据。
我尝试使用redirect快捷方式跳转到BookViewSet,代码示例:return redirect('/books/10', pk=book_id),原本期望浏览器发起GET请求,通过BookViewSet的retrieve方法返回更新后的Book数据,但实际BookViewSet仍收到PUT请求,被导向update方法,导致400错误。
问题
- 这是预期行为吗?
- 若为预期行为,如何在修改Page后通过GET请求获取更新后的Book视图?我不想将
retrieve方法的逻辑复制到update方法中。
我了解Post/Redirect/Get模式,但不确定需要哪些额外操作来实现GET请求。
PageViewSet代码
class PageViewSet(LoginRequiredMixin, viewsets.ModelViewSet): queryset = Page.objects.all() renderer_classes = [TemplateHTMLRenderer, JSONRenderer] serializer_class = PageSerializer def update(self, request, pk=None): page = self.get_object() page.update_layout( Layout.objects.get( id=request.data[f"{page.id}-layout"] ) # Form field prefix is set to page ID ) return redirect("book-detail", pk=page.book.id)
控制台日志
(django.request) log/log_response: Bad Request: /books/11/ [15/Oct/2024 13:14:40] "PUT /books/11/ HTTP/1.1" 400 15
解答
问题1:这不是预期行为
Django的redirect默认返回302状态码,标准浏览器处理302响应时,会自动用GET请求重定向后的地址。你遇到的PUT请求直接发送到Book地址的情况,大概率是前端AJAX请求未正确处理302响应,而是直接将原PUT请求的方法和内容转发到了目标地址。
问题2:解决方案
有两种可靠的方式实现需求:
方式一:返回303状态码强制使用GET请求
303状态码是Post/Redirect/Get模式的标准用法,它明确告诉客户端必须用GET请求访问重定向地址。可以手动构造响应:
from django.http import HttpResponse from django.urls import reverse def update(self, request, pk=None): page = self.get_object() page.update_layout( Layout.objects.get( id=request.data[f"{page.id}-layout"] ) ) # 构造303响应,强制客户端发起GET请求 response = HttpResponse(status=303) response['Location'] = reverse('book-detail', args=[page.book.id]) return response
方式二:直接返回Book的序列化数据(无需重定向)
如果不需要跳转页面,只是想返回更新后的Book数据,可以在update方法中直接序列化关联的Book并返回,复用BookSerializer的逻辑:
from rest_framework.response import Response from .serializers import BookSerializer def update(self, request, pk=None): page = self.get_object() page.update_layout( Layout.objects.get( id=request.data[f"{page.id}-layout"] ) ) # 获取关联Book并序列化返回 book = page.book serializer = BookSerializer(book, context={'request': request}) return Response(serializer.data)
另外需要注意:如果前端是通过AJAX发起PUT请求,需要确保前端代码能正确处理303响应(自动发起GET请求),或者直接接收方式二中返回的JSON数据。
内容的提问来源于stack exchange,提问作者charliesneath

