Source code for htmx_views.widgets
# -*- coding: utf-8 -*-
"""Create an htmx backed linked select widget."""
# Python imports
from urllib.parse import urlencode
# Django imports
from django.core.exceptions import ImproperlyConfigured
from django.forms.widgets import Select
from django.urls import reverse_lazy
from django.utils.text import format_lazy
# app imports
from ._optional import get_ajax_select_registry
registry = get_ajax_select_registry()
[docs]
class HTMXSelectWidget(Select):
"""Override SelectWidget to allow the options to be updated on a linked change.
This uses the machinery of django-ajax-select lookups to derive the list of options,
but uses htmx for the transfer mechanism.
"""
# temmplate_name = "htmx_views/widgets/select.html"
def __init__(self, lookup_channel, parent=None, *args, **kwargs):
"""Extract linked Select.
Args:
lookup_channel (str):
The name of an ajax_select lookup channel
"""
try:
self.lookup = registry.get(lookup_channel)
except ImproperlyConfigured as error:
raise ImproperlyConfigured(
f"Attempting to use a htmx_views widget with lookup channel {lookup_channel} that does not exist."
) from error
self.lookup_channel = lookup_channel
if parent is None:
parent = getattr(self.lookup, "parameter_name", None)
if parent is None:
raise ImproperlyConfigured(
f"Creating an htmx_views widget for {lookup_channel} without knowing the trigger."
)
self.parent_name = parent
endpoint = reverse_lazy("htmx_views:select", args=(self.lookup_channel,))
query_string = urlencode({"_htmx_parent": self.parent_name})
attrs = dict(kwargs.pop("attrs", {}))
attrs.update(
{
"hx-get": format_lazy("{}?{}", endpoint, query_string),
"hx-trigger": f"change from:#id_{self.parent_name}",
"hx-include": f"#id_{self.parent_name}",
}
)
kwargs["attrs"] = attrs
super().__init__(*args, **kwargs)
[docs]
def get_context(self, name, value, attrs):
"""Customise the context."""
attrs = dict(attrs or {})
attrs["hx-target"] = f"#id_{name}"
return super().get_context(name, value, attrs)