You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django表单/模板中无需JavaScript实现分类与子分类联动更新

嘿,这个需求我之前帮好几个开发者实现过,不用JavaScript的话,核心思路就是靠表单提交后的页面刷新,让Django后端动态过滤子分类的选项,完全依赖后端逻辑来处理关联关系。下面给你一步步拆解具体的实现步骤,都是可直接复用的代码:

1. 先定义你的模型结构

首先确保Category和SubCategory的关联关系是正确的,SubCategory通过外键绑定到Category:

from django.db import models

class Category(models.Model):
    name = models.CharField(max_length=100, verbose_name="分类")

    def __str__(self):
        return self.name

class SubCategory(models.Model):
    name = models.CharField(max_length=100, verbose_name="子分类")
    category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="subcategories")

    def __str__(self):
        return self.name
2. 自定义表单,支持动态过滤子分类

我们需要写一个自定义表单,重写__init__方法,接收category_id参数来过滤子分类的可选范围。默认情况下子分类下拉框是空的,只有传入分类ID后才会加载对应选项:

from django import forms
from .models import Category, SubCategory

class CategorySubcategoryForm(forms.Form):
    category = forms.ModelChoiceField(
        queryset=Category.objects.all(),
        empty_label="请选择分类",
        verbose_name="分类"
    )
    subcategory = forms.ModelChoiceField(
        queryset=SubCategory.objects.none(),  # 默认空集合
        empty_label="请先选择分类",
        verbose_name="子分类"
    )

    def __init__(self, *args, **kwargs):
        # 从参数中取出category_id,不传给父类的__init__
        category_id = kwargs.pop('category_id', None)
        super().__init__(*args, **kwargs)
        
        if category_id:
            # 根据分类ID过滤子分类
            self.fields['subcategory'].queryset = SubCategory.objects.filter(category_id=category_id)
            # 如果之前已经选中过子分类,保持选中状态
            if 'subcategory' in self.data:
                try:
                    sub_id = int(self.data.get('subcategory'))
                    self.fields['subcategory'].initial = sub_id
                except (ValueError, TypeError):
                    pass
3. 视图函数处理表单提交逻辑

视图要区分两种提交场景:一种是用户选完分类后点击“更新子分类”,另一种是选完子分类后点击“保存”。我们还可以用session来保存分类ID,避免用户刷新页面后子分类选项消失:

from django.shortcuts import render
from .forms import CategorySubcategoryForm

def category_subcategory_view(request):
    # 从session获取上次选中的分类ID,默认None
    category_id = request.session.get('selected_category')
    # 初始化表单,传入分类ID(如果有的话)
    form = CategorySubcategoryForm(category_id=category_id)

    if request.method == 'POST':
        if 'update_subcategory' in request.POST:
            # 用户点击了“更新子分类”按钮
            category_id = request.POST.get('category')
            # 把选中的分类ID存到session里
            request.session['selected_category'] = category_id
            # 重新初始化表单,加载对应子分类
            form = CategorySubcategoryForm(category_id=category_id)
        elif 'save_selection' in request.POST:
            # 用户点击了“保存”按钮,验证表单
            form = CategorySubcategoryForm(request.POST, category_id=category_id)
            if form.is_valid():
                # 这里写你的业务逻辑,比如保存到数据库或者处理数据
                selected_category = form.cleaned_data['category']
                selected_subcategory = form.cleaned_data['subcategory']
                # 示例:打印选中结果,实际替换成你的逻辑
                print(f"已选择:{selected_category.name} -> {selected_subcategory.name}")
                # 可选:保存后清除session里的分类ID
                # del request.session['selected_category']

    return render(request, 'category_form.html', {'form': form, 'category_id': category_id})
4. 模板渲染表单,引导用户操作

模板里要渲染表单,并且明确区分“更新子分类”和“保存”两个按钮——因为不用JS,用户必须手动点击按钮触发页面刷新来更新子分类选项:

<form method="post">
    {% csrf_token %}
    
    <!-- 分类选择区域 -->
    <div class="form-group">
        {{ form.category.label_tag }}
        {{ form.category }}
        <button type="submit" name="update_subcategory" class="btn btn-secondary">更新子分类</button>
    </div>

    <!-- 只有选中分类后才显示子分类选择区域 -->
    {% if category_id %}
    <div class="form-group mt-3">
        {{ form.subcategory.label_tag }}
        {{ form.subcategory }}
    </div>
    <button type="submit" name="save_selection" class="btn btn-primary mt-2">保存选择</button>
    {% endif %}
</form>
工作流程说明
  1. 用户第一次访问页面,只显示分类下拉框;
  2. 用户选好分类后,点击“更新子分类”按钮,表单提交到后端;
  3. 后端获取分类ID,过滤出对应的子分类,重新渲染页面,此时显示子分类下拉框和保存按钮;
  4. 用户选好子分类后,点击“保存”按钮,后端验证并处理表单数据;
  5. 用session保存分类ID,用户刷新页面后子分类选项不会消失。

这种方法的唯一缺点是需要页面刷新,但完全满足你“不用JavaScript”的要求,所有逻辑都在Django后端处理,稳定性很高。

内容的提问来源于stack exchange,提问作者Seshadri VS

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:41:32