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

Django Admin联动下拉菜单无法加载问题求助

Django Admin下拉菜单联动问题求助

需求:在Django Admin的Utenti表单中实现下拉联动——选择「provincia(省份)」后,自动过滤并填充「citta(城市)」下拉选项。

尝试过两种方案:添加jQuery监听下拉选择事件、自定义Widget,但均无效。选择父下拉时无任何反应,AJAX请求未触发(手动调用/get_cities/接口可正常返回数据)。

现有代码

Models.py

class Utenti(models.Model):
    nome = models.CharField(max_length = 188) 
    cognome = models.CharField(max_length = 188)
    dataNascita = models.DateField(max_length = 8, verbose_name = "Data di nascita")
    genere = models.CharField(max_length = 19, choices = generi, null = True, blank = True, verbose_name = "Genere")
    provincia = models.ForeignKey(Provincia, on_delete = models.SET_NULL, null = True)
    citta = models.ForeignKey(Comune, on_delete = models.SET_NULL, null = True, verbose_name = "Citta'")
    cf = models.CharField(max_length = 16, validators = [ validate_codice_fiscale ])
    

    class Meta:
        verbose_name = "Utente"
        verbose_name_plural = "Utenti"

    def __str__(self):
        return str(self.nome) + " " + str(self.cognome)

Admin.py

class FormUtenti(forms.ModelForm):
    class Meta:
        model = Utenti
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        province = [('','---------')] + [(i.codice_provincia, i.name) for i in Provincia.objects.all()]

        self.fields['citta'].queryset = Comune.objects.none()

        self.fields['provincia'].widget = forms.Select(
            attrs = {
                'onchange': 'filterCities(this.value);',
            },
            choices = province
        )

class UtentiAdmin(admin.ModelAdmin):
    form = FormUtenti

    class Media:
        js = (  
                'admin/js/jquery.init.js', // 尝试确保jQuery先加载
                'admin/js/getCities.js',   
             )

getCities.js

'use strict';
{
 const $ = django.jQuery;

 function filterCities(provincia_id){
    $.ajax({
            url:"/get_cities/",
            type:"POST",
            data:{provincia: provincia_id,},
            success: function(result) {
                this.alert(result);
                cls = document.getElementById("id_citta_select");
                cls.options.length = 0;
                for(var item in result) {
                    cls.options.add(new Option(item, result[item]));
                }
             },
             error: function(e) {
                 alert(JSON.stringify(e));
             },
    });
 }
}

views.py

def get_cities(request):
    test_request_log = TestRequestLog(test_request_message= request)
    test_request_log.save()
    provincia_id = request.POST.get('provincia')
    citta = Comune.objects.filter(provincia_id = provincia_id).order_by('name')
    return JsonResponse({
        "data": [{'citta': c.name} for c in citta],
    })

urls.py

path('get_cities/', views.get_cities, name='get_cities'),

问题排查与解决办法

  1. 修复JS函数作用域
    你的filterCities函数被包裹在块级作用域{}中,导致全局无法访问(Django Admin的onchange调用的是全局函数)。修改JS,将函数暴露到全局:

    'use strict';
    const $ = django.jQuery;
    
    // 将函数挂载到window对象,确保全局可访问
    window.filterCities = function(provincia_id){
        $.ajax({
                url:"/get_cities/",
                type:"POST",
                data:{
                    provincia: provincia_id,
                    // 必须携带CSRF令牌,否则Django会拦截POST请求
                    csrfmiddlewaretoken: $('input[name=csrfmiddlewaretoken]').val()
                },
                success: function(result) {
                    // 修正DOM选择器:Django生成的字段ID是id_citta,不是id_citta_select
                    const cittaSelect = document.getElementById("id_citta");
                    cittaSelect.options.length = 0;
                    // 适配返回的数据结构:遍历result.data数组
                    result.data.forEach(item => {
                        cittaSelect.options.add(new Option(item.citta, item.citta));
                    });
                 },
                 error: function(e) {
                     alert(JSON.stringify(e));
                 },
        });
    }
    
  2. 添加CSRF令牌
    Django默认会拦截未携带CSRF令牌的POST请求,必须在AJAX的data中加入csrfmiddlewaretoken,从页面的隐藏input中获取。

  3. 修正DOM选择器
    Django ModelForm生成的字段ID规则是id_<字段名>,所以子下拉的ID是id_citta,不是id_citta_select。

  4. 优化Admin Media加载
    Django Admin已经默认加载了jQuery,无需手动引入jquery.init.js,简化Media配置:

    class Media:
        js = ('admin/js/getCities.js',)
    
  5. 编辑场景初始化子下拉
    如果是编辑已有数据的页面,需要在Form的__init__方法中根据当前省份值初始化城市选项:

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        province = [('','---------')] + [(i.codice_provincia, i.name) for i in Provincia.objects.all()]
        self.fields['provincia'].widget = forms.Select(
            attrs={'onchange': 'filterCities(this.value);'},
            choices=province
        )
        # 编辑时自动加载对应城市
        if self.instance.pk and self.instance.provincia:
            self.fields['citta'].queryset = Comune.objects.filter(provincia_id=self.instance.provincia.codice_provincia)
        else:
            self.fields['citta'].queryset = Comune.objects.none()
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 09:25:59