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

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原生能力实现,无需额外安装包:

  1. 编写联动数据接口
    在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中。

  1. 自定义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可以大幅简化开发,还支持搜索过滤功能:

  1. 安装并配置库
pip install django-autocomplete-light

将dal、dal_select2添加到INSTALLED_APPS(注意放在django.contrib.admin之前)。

  1. 配置自动完成视图
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中。

  1. 在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 15:38:41