Перенаправление после выполнения PUT-запроса: могу ли я вместо этого перенаправить GET-запрос?

В моем Django-проекте есть основная модель Book и связанный с ней BookViewSet (из Django REST Framework). Из фронтенда можно редактировать другие объекты, например Page, который POSTs к соответствующему PageViewSet.

Изменения объектов Page также повлияют на Book, поэтому я хочу возвращать обновленный BookViewSet после обработки изменений в Page.

После обработки запроса PUT для страницы через PageViewSet я попробовал использовать ярлык redirect для отправки запроса в BookViewSet, например, так:

return redirect('/books/10', pk=book_id)

Я ожидал (и надеялся), что это будет GET-запрос, чтобы бэкэнд вернул обновленную книгу через BookViewSet с помощью метода «retrieve».

Однако, похоже, что BookViewSet по-прежнему получает PUT-запрос, что означает, что запрос будет отправлен в метод «update» BookViewSet.

  1. Ожидается ли это?

  2. Если да, то есть ли способ «получить» обновленное представление Book после внесения изменений в Page? Я могу добиться этого, поместив эту логику в метод «update» BookViewSet, но у меня уже есть такая логика в методе «retrieve», и я не хочу дублировать ее.

Недавно я нашел этот шаблон, описанный как «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)

После выполнения GET-запроса консоль показывает следующее:

(django.request) log/log_response: Bad Request: /books/11/
[15/Oct/2024 13:14:40] "PUT /books/11/ HTTP/1.1" 400 15

Да, это ожидаемое поведение.

Исправление немного зависит от того, как вы используете бэкэнд.

В Django redirect() по умолчанию установлен HTTP 302, что, если вы посмотрите на ссылку, означает, что клиенты, соответствующие спецификации HTTP, не будут менять свой HTTP-метод (то есть PUT сохраняется). Именно такое поведение вы и наблюдаете.

Если вы используете фронтенд, который вы контролируете, для вас не составит труда написать специальный случай, либо для конкретной страницы, либо в качестве промежуточного ПО, где вы будете принудительно менять его на GET всякий раз, когда вы следуете за 302s.

Но ваше сообщение как бы указывает на то, что вы используете либо браузер, либо чей-то другой клиент и его поведение по умолчанию. Если это так, то, возможно, оно соответствует спецификации? Если это так, то изменение ответа бэкенда на HTTP 303, вероятно, решит проблему, поскольку 303 специально создан для сценария, который вы описываете в этом сообщении.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections

В redirect()Django нет встроенных классов, которые бы выдавали HTTP 303, однако.

Вы можете попробовать передать status=303 в качестве аргумента, но я не уверен, что это сработает.

Если это не сработает, то следующим моим предложением будет создание соответствующего редиректа вручную с помощью DRF туземцев:

url = reverse("book-detail", pk=page.book.id)

return Response(
    status  = status.HTTP_303, 
    headers = {
        'Location': url
    }
)
Вернуться на верх