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

如何在Django admin中实现Country、Province、District三级联动下拉框

Django Admin 三级联动下拉框实现方案

第一步:定义关联模型

首先需要完成三个层级数据模型的外键关联,示例代码如下:

# models.py
from django.db import models

class Country(models.Model):
    name = models.CharField(max_length=100, verbose_name="国家")
    def __str__(self):
        return self.name

class Province(models.Model):
    name = models.CharField(max_length=100, verbose_name="省份")
    country = models.ForeignKey(Country, on_delete=models.CASCADE, verbose_name="所属国家")
    def __str__(self):
        return self.name

class District(models.Model):
    name = models.CharField(max_length=100, verbose_name="区县")
    province = models.ForeignKey(Province, on_delete=models.CASCADE, verbose_name="所属省份")
    def __str__(self):
        return self.name

# 引用这三个字段的业务模型示例
class Address(models.Model):
    country = models.ForeignKey(Country, on_delete=models.CASCADE, verbose_name="国家")
    province = models.ForeignKey(Province, on_delete=models.CASCADE, verbose_name="省份")
    district = models.ForeignKey(District, on_delete=models.CASCADE, verbose_name="区县")
    # 其余业务字段可自行补充

第二步:自定义Admin表单

在admin.py中重写业务模型的表单逻辑,控制初始下拉选项范围,同时注入联动脚本:

# admin.py
from django.contrib import admin
from django import forms
from .models import Address, Country, Province, District

class AddressForm(forms.ModelForm):
    class Meta:
        model = Address
        fields = "__all__"
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 新增状态下初始省、区县下拉为空
        if not self.instance.pk:
            self.fields["province"].queryset = Province.objects.none()
            self.fields["district"].queryset = District.objects.none()
        # 编辑状态下根据已保存值加载对应选项
        else:
            self.fields["province"].queryset = Province.objects.filter(country=self.instance.country)
            self.fields["district"].queryset = District.objects.filter(province=self.instance.province)

@admin.register(Address)
class AddressAdmin(admin.ModelAdmin):
    form = AddressForm
    # 注入联动JS
    class Media:
        js = ("js/address_linkage.js",)

第三步:编写联动JS逻辑

在项目静态文件目录下新建js/address_linkage.js,监听下拉框变化并通过ajax动态加载选项:

django.jQuery(document).ready(function() {
    // 国家切换时加载对应省份
    django.jQuery("#id_country").change(function() {
        const countryId = django.jQuery(this).val();
        django.jQuery.ajax({
            url: "/admin/load-provinces/",
            data: { country_id: countryId },
            success: function(data) {
                django.jQuery("#id_province").empty().append('<option value="">---------</option>');
                django.jQuery("#id_district").empty().append('<option value="">---------</option>');
                data.forEach(province => {
                    django.jQuery("#id_province").append(`<option value="${province.id}">${province.name}</option>`);
                });
            }
        });
    });

    // 省份切换时加载对应区县
    django.jQuery("#id_province").change(function() {
        const provinceId = django.jQuery(this).val();
        django.jQuery.ajax({
            url: "/admin/load-districts/",
            data: { province_id: provinceId },
            success: function(data) {
                django.jQuery("#id_district").empty().append('<option value="">---------</option>');
                data.forEach(district => {
                    django.jQuery("#id_district").append(`<option value="${district.id}">${district.name}</option>`);
                });
            }
        });
    });
});

注意:如果你的字段ID不是Django默认生成的id_country、id_province、id_district,需要将JS中的选择器替换为实际字段ID,静态文件路径需和项目配置匹配。

第四步:添加数据查询接口

在项目路由中新增两个接口,用于返回对应层级的选项数据:

# urls.py
from django.contrib import admin
from django.urls import path
from django.http import JsonResponse
from .models import Province, District

def load_provinces(request):
    country_id = request.GET.get("country_id")
    provinces = Province.objects.filter(country_id=country_id).values("id", "name")
    return JsonResponse(list(provinces), safe=False)

def load_districts(request):
    province_id = request.GET.get("province_id")
    districts = District.objects.filter(province_id=province_id).values("id", "name")
    return JsonResponse(list(districts), safe=False)

urlpatterns = [
    path("admin/", admin.site.urls),
    # 新增联动查询接口
    path("admin/load-provinces/", load_provinces),
    path("admin/load-districts/", load_districts),
]

内容的提问来源于stack exchange,提问作者Physics wala Boka

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 08:24:02