diff --git a/docs/source/multi-format.md b/docs/source/multi-format.md index 8a97125..90baf43 100644 --- a/docs/source/multi-format.md +++ b/docs/source/multi-format.md @@ -9,13 +9,13 @@ class PostsController < ApplicationController respond_to do |format| if @post.save - format.turbo_stream { render turbo_stream: turbo_stream.append(@post) } - format.json { render json: @post, status: :created } format.html { redirect_to @post, notice: 'Post was successfully created.' } + format.json { render json: @post, status: :created } + format.turbo_stream { render turbo_stream: turbo_stream.append(@post) } else - format.turbo_stream { render turbo_stream: turbo_stream.replace('new_post', partial: 'posts/form', locals: { post: @post }) } - format.json { render json: @post.errors, status: :unprocessable_entity } format.html { render :new } + format.json { render json: @post.errors, status: :unprocessable_entity } + format.turbo_stream { render turbo_stream: turbo_stream.replace('new_post', partial: 'posts/form', locals: { post: @post }) } end end end @@ -33,29 +33,34 @@ from turbo_helper import ( ) class TaskCreateView(LoginRequiredMixin, CreateView): - def form_valid(self, form): - response = super().form_valid(form) - request = self.request - messages.success(request, "Created successfully") - - with response_format(request) as resp_format: - if resp_format == ResponseFormat.TurboStream: - return TurboStreamResponse( - render_to_string( - "demo_tasks/partial/task_create_success.turbo_stream.html", - context={ - "form": TaskForm(), - }, - request=self.request, - ), - ) - else: - return response + def form_valid(self, form): + response = super().form_valid(form) + request = self.request + messages.success(request, "Created successfully") + + with respond_to(request) as resp_format: + if resp_format.html: + return response + if resp_format.turbo_stream: + return TurboStreamResponse( + render_to_string( + "demo_tasks/partial/task_create_success.turbo_stream.html", + context={ + "form": TaskForm(), + }, + request=self.request, + ), + ) ``` Notes: -1. If the client `Accept` turbo stream, we return turbo stream response. -2. If not, we return normal HTML response as fallback solution. +1. If the browser accepts HTML, we return HTML response. +2. If the browser accepts turbo stream, we return turbo stream response. 3. This is useful when we want to migrate our Django app from normal web page to turbo stream gradually. -4. If you are using Python 3.10+, you can use `match` statement instead of `if` statement. + +```{note} +Most browsers send Accept: `*/*` by default, so this would return True for all content types. + +To avoid problem, it is recommned to put resp_format.html logic at the top since the order matters. +``` diff --git a/src/turbo_helper/__init__.py b/src/turbo_helper/__init__.py index 5ef1a7a..afd4857 100644 --- a/src/turbo_helper/__init__.py +++ b/src/turbo_helper/__init__.py @@ -1,6 +1,5 @@ -from .constants import ResponseFormat from .response import HttpResponseSeeOther, TurboStreamResponse -from .shortcuts import redirect_303, response_format +from .shortcuts import redirect_303, respond_to from .stream import register_turbo_stream_action, turbo_stream from .templatetags.turbo_helper import dom_id @@ -14,6 +13,5 @@ "HttpResponseSeeOther", "redirect_303", "dom_id", - "response_format", - "ResponseFormat", + "respond_to", ] diff --git a/src/turbo_helper/shortcuts.py b/src/turbo_helper/shortcuts.py index 9117407..1031a2a 100644 --- a/src/turbo_helper/shortcuts.py +++ b/src/turbo_helper/shortcuts.py @@ -18,30 +18,34 @@ def redirect_303(to: Union[str, Model], *args, **kwargs) -> HttpResponseSeeOther return HttpResponseSeeOther(resolve_url(to, *args, **kwargs)) -def get_response_format(request): - """ - Inspired by Rails +def get_respond_to(request): + resp_format = ResponseFormat() - respond_to do |format| - format.turbo_stream { render turbo_stream: turbo_stream_template } - end - """ + # TODO: move logic to ResponseFormat class if request.accepts(TURBO_STREAM_MIME_TYPE): - return ResponseFormat.TurboStream - elif request.accepts("application/json"): - return ResponseFormat.JSON - else: - return ResponseFormat.HTML + resp_format.turbo_stream = True + + if request.accepts("application/json"): + resp_format.json = True + + if request.accepts("text/html"): + resp_format.html = True + + return resp_format @contextmanager -def response_format(request): +def respond_to(request): """ - Get supported response format from request headers + Inspired by Rails + + https://www.writesoftwarewell.com/how-respond_to-method-works-rails/ - html, json, turbo_stream + respond_to do |format| + format.turbo_stream { render turbo_stream: turbo_stream_template } + end """ - resp_format = get_response_format(request) + resp_format: ResponseFormat = get_respond_to(request) try: yield resp_format finally: