Skip to content

Instantly share code, notes, and snippets.

@geoffrey-eisenbarth
Last active September 11, 2024 19:44
Show Gist options
  • Save geoffrey-eisenbarth/e521497e032514791f7e29acd01daf7c to your computer and use it in GitHub Desktop.
Save geoffrey-eisenbarth/e521497e032514791f7e29acd01daf7c to your computer and use it in GitHub Desktop.
Django Middleware for django-template-partials
class HtmxPartialTemplateMiddleware:
"""Adds support for rendering partials with django-template-partials.
NOTES
-----
In order for this middleware to work properly, the following conditions must be met:
1) django-template-partials must be installed
2) Each template must have a partial defined directly in it with a name that coincides
with the HTML id of the element being swapped out. Since django-template-partials does
not support rendering partials defined in parent templates, you must be careful when
using Django's {% extends %} syntax.
3) One nice benefit of this method, is if you create partials surrounding your <forms> and
then set the `HX-Retarget` to e.g., `#form` in a view's `form_invalid()` method, then the
middleware will render the form errors and `HX-Retarget` will replace the submitted form
with validated form, showing the errors.
"""
DEFAULT_PARTIAL_NAME = 'main'
def __init__(self, get_response) -> None:
self.get_response = get_response
def __call__(self, request: HttpRequest) -> HttpResponse:
response = self.get_response(request)
if request.htmx and (response.status_code == 302):
response = HttpResponseClientRedirect(response.url)
return response
def process_template_response(self, request, response):
if request.htmx:
if retarget_id := response.get('HX-Retarget'):
partial_name = retarget_id.strip('#')
else:
partial_name = request.htmx.target or self.DEFAULT_PARTIAL_NAME
response.template_name = [
f'{template_name}#{partial_name}'
if '#' not in template_name
else template_name
for template_name in response.template_name
]
return response
@geoffrey-eisenbarth
Copy link
Author

Some notes about django-template-partials and this middleware:

  1. This works great if you want to use Django's built in generic class-based views (or subclasses thereof)
  2. If not being able to render partials defined in parent templates is a deal breaker for you, consider using django-render-block, although a middleware like this would not work as well since django-render-block does not tie into the template rendering system (so a middleware with process_template_response would not work, since the view would not being returning a TemplateResponse object). It's probably best to use django-render-block if you're using function based views.

I'm by no means an expert at this stuff, simply documenting what I've learned trying to combine HTMX and class-based views. If you have any thoughts, suggestions, questions, or enhancements, I'd love to hear them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment