diff --git a/src/servala/frontend/forms/mixins.py b/src/servala/frontend/forms/mixins.py new file mode 100644 index 0000000..a2f61a6 --- /dev/null +++ b/src/servala/frontend/forms/mixins.py @@ -0,0 +1,15 @@ +class HtmxMixin: + """ + Form mixin that retains only a single field when specified. + Useful when sending single fields with htmx. + """ + + def __init__(self, *args, **kwargs): + self.single_field = kwargs.pop("single_field", None) + + super().__init__(*args, **kwargs) + + if self.single_field and self.single_field in self.fields: + field = self.fields[self.single_field] + self.fields.clear() + self.fields[self.single_field] = field diff --git a/src/servala/frontend/views/mixins.py b/src/servala/frontend/views/mixins.py new file mode 100644 index 0000000..3184e86 --- /dev/null +++ b/src/servala/frontend/views/mixins.py @@ -0,0 +1,36 @@ +from django.utils.functional import cached_property +from django.views.generic import TemplateView + + +class HtmxMixin(TemplateView): + fragments = [] + + @cached_property + def is_htmx(self): + return self.request.headers.get("HX-Request") + + def _get_fragment(self): + if self.request.method == "POST": + fragment = self.request.POST.get("fragment") + else: + fragment = self.request.GET.get("fragment") + if fragment and fragment in self.fragments: + return fragment + + def get_template_names(self): + template_names = super().get_template_names() + if self.is_htmx and (fragment := self._get_fragment()): + return [f"{template_names[0]}#{fragment}"] + return template_names + + def get_form_kwargs(self): + result = super().get_form_kwargs() + if self.is_htmx and (field_name := self.request.POST.get("hx-single-field")): + result["single_field"] = field_name + return result + + def form_valid(self, form): + result = super().form_valid(form) + if self.is_htmx and self._get_fragment(): + return self.get(self.request, *self.args, **self.kwargs) + return result