如何实现表单二级联动:根据首个字段值过滤第二个字段选项
Django表单实现城市-商家二级联动方案
1. 补充关联模型定义
首先确保你有城市和商家的关联模型,示例如下(可根据实际需求调整):
from django.db import models from .models import MyUser class City(models.Model): name = models.CharField(max_length=100, unique=True) def __str__(self): return self.name class Merchant(models.Model): name = models.CharField(max_length=200) city = models.ForeignKey(City, on_delete=models.CASCADE, related_name='merchants') user = models.ForeignKey(MyUser, on_delete=models.RESTRICT) def __str__(self): return self.name
2. 修改表单类适配联动逻辑
以你现有ExcludedDateForm为基础,扩展支持城市-商家联动:
class ExcludedDateForm(ModelForm): class Meta: model = models.ExcludedDate exclude = ('user', 'recurring',) fields = ['city', 'merchant'] # 明确添加联动字段 def __init__(self, user=None, **kwargs): super().__init__(**kwargs) if user: # 过滤当前用户可访问的城市 self.fields['city'].queryset = models.City.objects.filter(merchants__user=user).distinct() # 初始化商家选项:根据当前城市过滤或设为空 merchant_queryset = models.Merchant.objects.none() # 处理表单提交时的城市值 if 'city' in self.data: try: city_id = int(self.data.get('city')) merchant_queryset = models.Merchant.objects.filter(city_id=city_id, user=user) except (ValueError, TypeError): pass # 处理编辑场景的初始值 elif self.instance.pk: merchant_queryset = self.instance.city.merchants.filter(user=user) self.fields['merchant'].queryset = merchant_queryset
3. 添加后端接口返回商家选项
新增视图用于异步获取指定城市的商家:
from django.http import JsonResponse from django.views.decorators.http import require_GET from django.contrib.auth.decorators import login_required from .models import Merchant @login_required @require_GET def get_merchants_by_city(request): city_id = request.GET.get('city_id') if not city_id: return JsonResponse({'options': []}) try: merchants = Merchant.objects.filter(city_id=city_id, user=request.user).values('id', 'name') options = [{'value': m['id'], 'text': m['name']} for m in merchants] return JsonResponse({'options': options}) except ValueError: return JsonResponse({'options': []})
在urls.py中添加路由:
from django.urls import path from .views import get_merchants_by_city urlpatterns = [ # 其他路由... path('get-merchants/', get_merchants_by_city, name='get_merchants_by_city'), ]
4. 前端模板添加联动交互
在表单模板中加入JavaScript监听城市选择事件,动态更新商家选项:
<form method="post"> {% csrf_token %} {{ form.as_p }} <button type="submit">提交</button> </form> <script> const citySelect = document.getElementById('id_city'); const merchantSelect = document.getElementById('id_merchant'); citySelect.addEventListener('change', function() { const cityId = this.value; merchantSelect.innerHTML = '<option value="">请选择商家</option>'; if (!cityId) return; fetch(`{% url 'get_merchants_by_city' %}?city_id=${cityId}`) .then(res => res.json()) .then(data => { data.options.forEach(opt => { const option = document.createElement('option'); option.value = opt.value; option.textContent = opt.text; merchantSelect.appendChild(option); }); }) .catch(err => console.error('加载商家失败:', err)); }); </script>
核心逻辑说明
- 权限过滤:所有城市、商家查询都关联当前用户,和你现有
FilterByUserMixin的逻辑保持一致 - 初始化兼容:覆盖新增、编辑两种场景的商家选项初始化
- 无刷新联动:通过前端异步请求实现选项动态更新,提升用户体验
内容的提问来源于stack exchange,提问作者Den Silinskii
相关产品推荐
相关产品推荐

