在使用ajax,我们经常做一个案例:页面加载时,将全国的省份加载进来;若选择省份,则出现本省的所有地市;若选择地市,则出现本地市的所有区县。
今天我用Django和ajax来实现。
第一步:设计数据模型
为了减少表的数量,我设计了一张带有父id的表,为了简单,也有level(0位省份、2地市、3区县)
class Address_Province(models.Model): code = models.CharField(max_length=20, verbose_name="编码", primary_key=True) name = models.CharField(max_length=100, verbose_name="名称") parent_code = models.ForeignKey("self", blank=True, null=True, verbose_name="上级名称") level = models.IntegerField(verbose_name="级别,1省份 2地市 3区县") class Meta: db_table = "Address_Province" verbose_name = "中国省市县" verbose_name_plural = verbose_name ordering = [‘code‘] def __str__(self): return self.name
models
同时准备数据
第二步:Html代码
仅仅是搭建一个web结构
<div class="container"> <div class="row"> <label for="prov">省份</label> <select id="prov" name="prov"> <option id="-1">请选择</option> </select> <br> <label for="city">地市</label> <select id="city" name="city"> <option id="-1">请选择</option> </select> <br> <label for="country">区县</label> <select id="country" name="county"> <option id="-1">请选择</option> </select> </div> </div>
html
第三步:将省份信息在页面加载时就显示出下拉菜单
这里我们借助JQuery来实现,
$(function () { $("#country option:gt(0)").remove(); /*初始化省份信息*/ $.get("/junit/getProv/", function (data) { var result = JSON.parse(data); $.each(result, function (code, item) { liNode = $("<option></option>").attr("id", item.code).text(item.name); $("#prov").append(liNode); }) }); }
我们从后端得到的json串是字符串,而不是Object对象。这时我们必须将字符串转成json对象,这里有两个方法:
1、JSON.parse(data) 原始的方法
2、$.parseJSON(data) jquery方法
$.each是jquery的方法,目的是遍历集合result,针对集合中每个元素,执行回调方法。
$("<option></option>").attr("id", item.code).text(item.name);创建对象,同时给对象加上属性和文字
append将option标签加入到省份的select上。
那么前端的数据从哪里来的呢?当然是后台传过来的
第四步:创建取省份信息的视图函数
def getProv(request): # json.dumps(data) # TypeError: Object of type ‘QuerySet‘ is not JSON serializable data = Address_Province.objects.values("code", "name").filter(level=0).all() print(data) return HttpResponse(json.dumps(list(data)))
注意将对象转成字符串的方法:json.dumps应用到QuerySet对象时会报错,我只要转成list就正常。
至此,省份信息能在页面上正常显示。
第五步:点击省份显示地市
假如我点击某个省份,在地市下拉选中显示本省的所有地市。那对省份下拉选触发了点击事件,增加$("#prov").click(fn)合适吗?假如我连续选择相同的省份,实际上没有必要触发事件。这样我们使用change事件更加合适些。$("#prov").change
我怎么把省份id信息传递到后台呢??我们需要得到变更后的id
var prov_code = $(this).find("option:selected").attr("id");
使用$.get来执行ajax函数,同时传送所选省份参数
$("#prov").change(function () { var prov_code = $(this).find("option:selected").attr("id"); $.get("/junit/getCity/", {prov_selected: prov_code}, function (data) { var result = parse(data); $.each(result, function (code, item) { var liNode = $("<option></option>").attr("id", item.code).text(item.name); $("#city").append(liNode); }) }); });
第六步:依据省份得到地市信息
def getCity(request): prov_selected = request.GET.get("prov_selected") data = Address_Province.objects.values("code", "name").filter(level=2, parent_code=prov_selected).all() return HttpResponse(json.dumps(list(data)))
这里查询数据使用Django自带的orm工具。
对于点击地市得到区县,采用同样的处理方法
第七步:及时清理原来的下拉选内容
我们点击山东,再次点击河北,会发现地市下拉选同时包括两个省份的地市信息,这就不对啦。我们在展示地市信息时,需要先清理原来的地市。
$("#prov").change(function () { var prov_code = $(this).find("option:selected").attr("id"); $("#city option:gt(0)").remove(); $("#country option:gt(0)").remove(); $.get("/junit/getCity/", {prov_selected: prov_code}, function (data) { var result = parse(data); $.each(result, function (code, item) { var liNode = $("<option></option>").attr("id", item.code).text(item.name); $("#city").append(liNode); }) }); });
以上我使用$.get来实现,下一步我们使用$.post来实现,会有意想不到的错误。未完待续……