Django HTMX实现单选框选择后动态修改hx-get URL
使用Django结合HTMX开发时,OrderForm中以RadioSelect作为venue字段组件,初始化表单时hx-get的URL里slug值为None,点击单选按钮时请求路径变为planner/detail/None/,触发路由匹配错误。需要实现单选框选择变化时,动态将URL中的None替换为所选venue的slug或id,优先采用HTMX/Django原生方案,也可接受Alpine.js实现。
现有代码:
forms.py:
class OrderForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() self.helper.attrs = { "hx_get": reverse("planner:detail", kwargs={"slug": self.instance.venue}), "hx_target": "#services", } class Meta: model = Order fields = ["venue"] widgets = {"venue": forms.RadioSelect()}
HTML:
<div id="venue-options" class="p-3"> {% crispy form %} </div>
方案一:HTMX + Django 原生实现(推荐)
核心思路是不在表单级别绑定hx-get,而是给每个单选按钮单独设置动态的hx-get属性,利用Django模板渲染时直接获取每个venue的slug生成正确URL,同时通过HTMX的hx-trigger监听选择变化。
修改forms.py
移除表单级别的hx-get配置,改为在模板中单独处理每个单选按钮:
class OrderForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() # 移除表单级hx-get,改为模板中为每个单选框单独设置 class Meta: model = Order fields = ["venue"] widgets = {"venue": forms.RadioSelect()}
修改HTML模板
手动遍历单选框选项,为每个<input>添加HTMX属性:
<div id="venue-options" class="p-3"> {% for choice in form.venue %} <div class="form-check"> <input class="form-check-input" type="radio" name="{{ form.venue.name }}" id="{{ choice.id_for_label }}" value="{{ choice.value }}" hx-get="{% url 'planner:detail' slug=choice.choice_value.slug %}" hx-trigger="change" hx-target="#services" {% if choice.is_selected %}checked{% endif %} > <label class="form-check-label" for="{{ choice.id_for_label }}"> {{ choice.choice_label }} </label> </div> {% endfor %} </div>
choice.choice_value对应单选框绑定的Venue实例,直接取其slug生成正确请求路径,初始化和选择变化时都不会出现None问题。
方案二:HTMX 动态URL参数(利用hx-vars)
若不想手动遍历单选框,可保留表单级HTMX属性,通过hx-vars动态传递选中的venue标识,配合URL占位符实现动态路径。
修改forms.py
将hx-get设为带占位符的路径,同时配置hx-vars传递选中值:
class OrderForm(forms.ModelForm): # 若需要单选框value为slug,重写字段配置 venue = forms.ModelChoiceField( queryset=Venue.objects.all(), widget=forms.RadioSelect(), to_field_name='slug' # 将单选框value设为slug,默认是id ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() self.helper.attrs = { "hx-get": "/planner/detail/{slug}/", # 用{slug}作为占位符 "hx-target": "#services", "hx-vars": "slug: document.querySelector('input[name=\"venue\"]:checked')?.value", "hx-trigger": "change" } class Meta: model = Order fields = ["venue"]
HTMX会自动将hx-vars中的slug值替换到hx-get的占位符位置,生成正确请求路径。
方案三:Alpine.js 动态修改hx-get
若需要更灵活的控制逻辑,可借助Alpine.js监听单选框变化,实时更新表单的hx-get属性。
修改HTML模板
<div id="venue-options" class="p-3" x-data="{ selectedSlug: '{{ form.instance.venue.slug if form.instance.venue else '' }}' }"> {% crispy form %} <script> // 初始化表单hx-get const form = document.querySelector('#venue-options form'); form.setAttribute('hx-get', `/planner/detail/${selectedSlug}/`); // 监听单选框选择变化 document.querySelectorAll('input[name="venue"]').forEach(radio => { radio.addEventListener('change', function() { // 若单选框value是id,需额外查询slug;此处假设value为slug form.setAttribute('hx-get', `/planner/detail/${this.value}/`); }); }); </script> </div>
需确保单选框的value为venue的slug,若默认是id,可通过AJAX查询对应slug后再更新URL。
内容的提问来源于stack exchange,提问作者Restea Elias

