Skip to content

Commit

Permalink
update multi-format (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
michael-yin authored Jan 18, 2024
1 parent 306263b commit 3c39f9c
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 45 deletions.
55 changes: 30 additions & 25 deletions docs/source/multi-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
```
6 changes: 2 additions & 4 deletions src/turbo_helper/__init__.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -14,6 +13,5 @@
"HttpResponseSeeOther",
"redirect_303",
"dom_id",
"response_format",
"ResponseFormat",
"respond_to",
]
36 changes: 20 additions & 16 deletions src/turbo_helper/shortcuts.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down

0 comments on commit 3c39f9c

Please sign in to comment.