110 lines
3.6 KiB
Python
110 lines
3.6 KiB
Python
import json
|
|
|
|
from django.http import HttpResponse
|
|
from django.template.loader import render_to_string
|
|
from django_htmx.http import HttpResponseClientRedirect, HttpResponseClientRefresh
|
|
|
|
class HTMXPartialMixin:
|
|
"""Mixin that automatically switches to a partial template when the request is made via HTMX."""
|
|
|
|
partial_template_name = None
|
|
|
|
def get_template_names(self):
|
|
# If HTMX request and a partial is defined, return it
|
|
if getattr(self.request, "htmx", False) and self.partial_template_name:
|
|
return [self.partial_template_name]
|
|
|
|
return super().get_template_names()
|
|
|
|
|
|
class HTMXViewMixin:
|
|
"""
|
|
A full-featured HTMX integration mixin for Django CBVs.
|
|
Supports:
|
|
- partial rendering via django-partials
|
|
- HX-Redirect
|
|
- HX-Push-URL
|
|
- HX-Trigger events
|
|
- HX-Refresh
|
|
- Graceful fallback to normal Django rendering
|
|
"""
|
|
|
|
# Name of the partial block: template.html#partial_name
|
|
partial_name = None
|
|
|
|
# Optional: automatically push URL on GET
|
|
htmx_push_url = None
|
|
|
|
# Optional: name of the menu item to highlight
|
|
menu_highlight = None
|
|
|
|
# Optional: trigger events after rendering
|
|
htmx_trigger = None
|
|
htmx_trigger_after_settle = None
|
|
htmx_trigger_after_swap = None
|
|
|
|
# Optional: redirect target for HTMX
|
|
htmx_redirect_url = None
|
|
|
|
# Optional: refresh the page
|
|
htmx_refresh = False
|
|
|
|
def render_partial(self, context):
|
|
"""Render a django-partials block."""
|
|
request = self.request
|
|
return render_to_string(self.partial_name, context, request=request)
|
|
|
|
def apply_htmx_headers(self, response):
|
|
"""Attach HX-* headers to the response."""
|
|
request = self.request
|
|
|
|
if request.htmx:
|
|
is_get = request.method == "GET"
|
|
is_pagination = "page" in request.GET
|
|
|
|
if is_get and not is_pagination:
|
|
# Push the current path unless overridden
|
|
response.headers["HX-Push-Url"] = self.htmx_push_url or request.get_full_path()
|
|
|
|
print(response.headers)
|
|
|
|
# Build HX-Trigger payload
|
|
trigger_payload = {}
|
|
|
|
# 1. User-defined triggers
|
|
if self.htmx_trigger:
|
|
trigger_payload.update(json.loads(self.htmx_trigger))
|
|
|
|
# 2. Auto menu highlight trigger
|
|
if self.menu_highlight:
|
|
trigger_payload["menuHighlight"] = self.menu_highlight
|
|
|
|
# Emit HX-Trigger if anything is present
|
|
if trigger_payload:
|
|
response.headers["HX-Trigger"] = json.dumps(trigger_payload)
|
|
|
|
if self.htmx_trigger_after_settle:
|
|
response.headers["HX-Trigger-After-Settle"] = self.htmx_trigger_after_settle
|
|
|
|
if self.htmx_trigger_after_swap:
|
|
response.headers["HX-Trigger-After-Swap"] = self.htmx_trigger_after_swap
|
|
|
|
return response
|
|
|
|
def render_to_response(self, context, **response_kwargs):
|
|
"""Renders HTMX response, applying headers and handling directives"""
|
|
request = self.request
|
|
|
|
if not request.htmx:
|
|
response = super().render_to_response(context, **response_kwargs)
|
|
return self.apply_htmx_headers(response)
|
|
|
|
if self.htmx_redirect_url:
|
|
return HttpResponseClientRedirect(self.htmx_redirect_url)
|
|
|
|
if self.htmx_refresh:
|
|
return HttpResponseClientRefresh()
|
|
|
|
html = self.render_partial(context)
|
|
response = HttpResponse(html, **response_kwargs)
|
|
return self.apply_htmx_headers(response) |