Django Admin中实现国家/州/城市联动下拉框方案咨询
Django Admin 国家-州-城市联动下拉框解决方案
核心前提
首先确保数据模型的关联逻辑正确:
from django.db import models class Country(models.Model): name = models.CharField(max_length=100) def __str__(self): return self.name class State(models.Model): name = models.CharField(max_length=100) country = models.ForeignKey(Country, on_delete=models.CASCADE) def __str__(self): return self.name class City(models.Model): name = models.CharField(max_length=100) state = models.ForeignKey(State, on_delete=models.CASCADE) def __str__(self): return self.name
方案1:原生JS + AJAX(无第三方依赖)
完全基于Django原生能力实现,无需额外安装包:
- 编写联动数据接口
在app中添加视图,根据父级ID返回子级选项:
from django.http import JsonResponse from .models import State, City def get_states(request): country_id = request.GET.get('country_id') states = State.objects.filter(country_id=country_id).values('id', 'name') return JsonResponse(list(states), safe=False) def get_cities(request): state_id = request.GET.get('state_id') cities = City.objects.filter(state_id=state_id).values('id', 'name') return JsonResponse(list(cities), safe=False)
将这两个视图配置到项目的urls.py中。
- 自定义Admin表单与模板
重写Admin表单并注入JS逻辑:
from django.contrib import admin from django import forms from .models import City class CityAdminForm(forms.ModelForm): class Meta: model = City fields = '__all__' widgets = { 'country': forms.Select(attrs={'id': 'id_country'}), 'state': forms.Select(attrs={'id': 'id_state'}), 'city': forms.Select(attrs={'id': 'id_city'}), } @admin.register(City) class CityAdmin(admin.ModelAdmin): form = CityAdminForm change_form_template = 'admin/city_change_form.html'
创建templates/admin/city_change_form.html模板:
{% extends "admin/change_form.html" %} {% block extrajs %} {{ block.super }} <script> // 国家联动州 document.getElementById('id_country').addEventListener('change', function() { const countryId = this.value; const stateSelect = document.getElementById('id_state'); stateSelect.innerHTML = '<option value="">---------</option>'; if (countryId) { fetch(`/get-states/?country_id=${countryId}`) .then(res => res.json()) .then(data => { data.forEach(state => { const option = document.createElement('option'); option.value = state.id; option.textContent = state.name; stateSelect.appendChild(option); }); }); } }); // 州联动城市 document.getElementById('id_state').addEventListener('change', function() { const stateId = this.value; const citySelect = document.getElementById('id_city'); citySelect.innerHTML = '<option value="">---------</option>'; if (stateId) { fetch(`/get-cities/?state_id=${stateId}`) .then(res => res.json()) .then(data => { data.forEach(city => { const option = document.createElement('option'); option.value = city.id; option.textContent = city.name; citySelect.appendChild(option); }); }); } }); </script> {% endblock %}
方案2:第三方库快速实现(推荐)
使用django-autocomplete-light可以大幅简化开发,还支持搜索过滤功能:
- 安装并配置库
pip install django-autocomplete-light
将dal、dal_select2添加到INSTALLED_APPS(注意放在django.contrib.admin之前)。
- 配置自动完成视图
from dal import autocomplete from .models import State, City class StateAutocomplete(autocomplete.Select2QuerySetView): def get_queryset(self): if not self.request.user.is_authenticated: return State.objects.none() qs = State.objects.all() country_id = self.forwarded.get('country', None) if country_id: qs = qs.filter(country_id=country_id) return qs class CityAutocomplete(autocomplete.Select2QuerySetView): def get_queryset(self): if not self.request.user.is_authenticated: return City.objects.none() qs = City.objects.all() state_id = self.forwarded.get('state', None) if state_id: qs = qs.filter(state_id=state_id) return qs
将视图配置到urls.py中。
- 在Admin中集成
from django.contrib import admin from dal import autocomplete from django import forms from .models import City class CityAdminForm(forms.ModelForm): class Meta: model = City fields = '__all__' widgets = { 'state': autocomplete.ModelSelect2( url='state-autocomplete', forward=['country'], ), 'city': autocomplete.ModelSelect2( url='city-autocomplete', forward=['state'], ), } @admin.register(City) class CityAdmin(admin.ModelAdmin): form = CityAdminForm
思路指引
- 若项目要求无第三方依赖,优先选择方案1,完全可控但需要自行编写JS和视图逻辑。
- 若追求开发效率和更好的用户体验(如搜索下拉),方案2的
django-autocomplete-light是最优选择,尤其适合数据量大的场景。 - 无论哪种方案,都要确保Admin静态文件加载正常,比如方案1中的JS需正确注入到模板。
内容的提问来源于stack exchange,提问作者oneclick
相关产品推荐
相关产品推荐

