需求

在项目中使用Django admin作为后台管理,在店铺表编辑页面需要设置省市区三级联动,查了很多资源,没有发现原生支持的联动方式。 在网上借鉴了别人的写法。

配置:

  • Python 3.7

  • Django3.1

步骤

  1. 在app文件下创建templates文件夹,创建shop_detail.html用于复写店铺编辑页。 文件名字可自定义。

  2. 在app的admin.py中增加并注册ShopAdmin类,在类中增加change_form_template = "shop_detail.html",指向的html文件名和1中的一致。

  3. 从当前python环境中,找到Django包,复制里面的chang_form.html全部内容到前面创建的html中。 该html具体地址为python3.7/site-packages/django/contrib/admin/templates/admin/change_form.html

  4. 编辑shop_detail.html

  • 将以下ajax请求增加到替换到admin_change_form_document_ready的block中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
{% block admin_change_form_document_ready %}
<script type="text/javascript">
(function($) {
$(document).ready(function() {
$('.add-another').click(function(e) {
e.preventDefault();
var event = $.Event('django:add-another-related');
$(this).trigger(event);
if (!event.isDefaultPrevented()) {
showAddAnotherPopup(this);
}
});

{% if adminform and add %}
$('form#{{ opts.model_name }}_form :input:visible:enabled:first').focus()
{% endif %}
});
$.get('/common/choose/province/',function(p_info){ // 获取省份接口 这里的地址自定义,已实际的接口地址为准
var province_info = $('#id_province').empty().append('<option value>'+'---------'+'</option>');
// id_province 已实际的省份字段名为准,可在admin页面中查找
$.each(p_info.p_lists,function(i,province){
province_info.append('<option value="'+province.p_id+'">'+province.p_name+'</option>')
});

$('#id_province').change(function(){
p_id = $(this).val();
$.get('/common/choose/city/',{'p_id':p_id},function(c_info){ // 获取城市接口 这里的地址自定义,已实际的接口地址为准
var city_info = $('#id_city').empty().append('<option value>'+'---------'+'</option>');
// id_city 已实际的城市字段名为准,可在admin页面中查找
$.each(c_info.c_lists,function(i,city){
city_info.append('<option value="'+city.c_id+'">'+city.c_name+'</option>')
})
})
});

$('#id_city').change(function(){
c_id = $(this).val();
$.get('/common/choose/area/',{"c_id": c_id},function(a_info){ // 获取地区接口 这里的地址自定义,已实际的接口地址为准
var area_info = $('#id_district').empty().append('<option value>'+'---------'+'</option>');
// id_district 已实际的地区字段名为准,可在admin页面中查找
$.each(a_info.a_lists,function(i,area){
area_info.append('<option value="'+area.a_id+'">'+area.a_name+'</option>')
});
});
});
});
})(django.jQuery);
</script>
{% endblock %}
  • 注意里面的省市区字段在page页面中的类名,不确定的话可通过浏览器开发者工具查找。

  • 增加省市区调用接口, 我的地区表在common模块中,同时为避免接口地址冲突,增加了 /choose字段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def choose_province(request):
"""查询省"""
provinces = Area.objects.filter(parent=None)
p_lists = [{"p_id": province.id, "p_name": province.name} for province in provinces]
p_info = {"p_lists": p_lists}
return JsonResponse(p_info, safe=False)


def choose_city(request):
"""查询市"""
p_id = request.GET.get('p_id')
print(p_id, '--------')
if not p_id:
return JsonResponse({}, safe=False)
citys = Area.objects.filter(parent_id=p_id)
c_lists = [{"c_id": city.id, "c_name": city.name} for city in citys]
print(c_lists[0:5])
c_info = {"c_lists": c_lists}
return JsonResponse(c_info, safe=False)


def choose_area(request):
"""查询区"""
c_id = request.GET.get('c_id')
print(c_id, '=======')
if not c_id:
return JsonResponse({}, safe=False)
areas = Area.objects.filter(parent_id=c_id)
print(areas)
a_lists = [{"a_id": area.id, "a_name": area.name} for area in areas]
a_info = {"a_lists": a_lists}
print(a_info)
return JsonResponse(a_info, safe=False)
  • URL地址设置。
1
2
3
url(r'^choose/province/$', views.choose_province),
url(r'^choose/city/$', views.choose_city),
url(r'^choose/area/$', views.choose_area),

总结

Django admin功能比较齐全,以前只是按照文档使用,并没有涉及到修改页面。 通过这个3级联动,也清楚了admin的部分原理。对于一些复杂的页面,可以用自定义的形式来重构页面。