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'),
问题排查与解决办法
修复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)); }, }); }添加CSRF令牌
Django默认会拦截未携带CSRF令牌的POST请求,必须在AJAX的data中加入csrfmiddlewaretoken,从页面的隐藏input中获取。修正DOM选择器
Django ModelForm生成的字段ID规则是id_<字段名>,所以子下拉的ID是id_citta,不是id_citta_select。优化Admin Media加载
Django Admin已经默认加载了jQuery,无需手动引入jquery.init.js,简化Media配置:class Media: js = ('admin/js/getCities.js',)编辑场景初始化子下拉
如果是编辑已有数据的页面,需要在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
相关产品推荐
相关产品推荐

