Django项目实践(商城):十一、补充:地址三级联动

在这里插入图片描述

(根据居然老师直播课内容整理)

一、准备工作

  • 创建app应用: addresses
D:\pythonprogram\django_project\lgshop\apps> python ..\manage.py startapp addresses
  • 注册app
    在这里插入图片描述

二、地址三级联动数据库设计

  • 省、市、县三级地址保存方式有两种:
    • 方式一:省、市、县存放在三个表中,根据外键进行关联
      在这里插入图片描述
    • 方式二:省、市、县存放在一个表中,利用外键关联自己实现
      在这里插入图片描述
  • 本次采用方式二:省市县存放一个表中实现
# /apps/addresses/models.py
from django.db import models

class Area(models.Model):
    name=models.CharField(max_length=20,verbose_name="名称")
    parent=models.ForeignKey("self",on_delete=models.SET_NULL,null=True,blank=True,verbose_name="上级",related_name="subs")

    class Meta:
        db_table="tb_areas"
        verbose_name="省市区"
        verbose_name_plural=verbose_name

    def __str__(self):
        return self.name
  • 数据迁移
    在这里插入图片描述
python manage.py makemigrations
python manage.py migrate

三、实现方式

在这里插入图片描述

  • 页面加载完成时,会自动向后端发起查询省份信息,并将结果插入省份下拉列表中
  • 当省份信息变更时,通过ajax将省id发向后端发起市州查询,并将结果插入市州下拉列表中
  • 当市州信息变更时,通过ajax将市id发向后端发起区县查询,并将结果插入区县下拉列表中
  • 从而实现省市县三级联动

四、省份列表获取

1、前端实现

  • 当页面加载完成时,自动向后端发起省份查询
    在这里插入图片描述
    在这里插入图片描述

2、接口设计和定义

2.1 请求方式:

选项 方案
请求方法 get
请求地址 /areas/

2.2 请求参数 :

参数名 类型 是否必传 说明
area_id string

2.3 响应结果 : json

响应结果 响应内容
code 状态码
errmsg 错误信息
province_list 列表, 每个元素是一个字典 :{“id”:省份id, “name”:省份名称}
  • 省份数据JSON
{
    
    
  "code":"0",
  "errmsg":"OK",
  "province_list":[
      {
    
    
          "id":110000,
          "name":"北京市"
      },
      {
    
    
          "id":120000,
          "name":"天津市"
      },
      {
    
    
          "id":130000,
          "name":"河北省"
      },
      ......
  ]
}

3、后端view实现

  • 获取参数
  • 如果参数为空,表示查询省份信息
    • 通过Areas类,获取parent为空的数据集
    • 循环读取数据集,生成省份信息列表
      • 省份信息字典:{“id”:省份id,“name”:省份名称}
    • 返回前端json数据
      在这里插入图片描述

4、后端路由定义

4.1 总路由

  • lgshop/urls.py
    在这里插入图片描述

4.2 子路由

在这里插入图片描述

五、市列表获取

1、前端实现

  • 选择的省份信息发生变化后,将省份id发向后端发起市查询
    在这里插入图片描述

2、接口设计和定义

2.1 请求方式:

选项 方案
请求方法 get
请求地址 /areas/

2.2 请求参数 :

参数名 类型 是否必传 说明
area_id string 省id

2.3 响应结果 : json

响应结果 响应内容
code 状态码
errmsg 错误信息
sub_data 字典
  • 市数据JSON
{
    
    
	"code":"0",
	"errmsg":"OK",
	"sub_data":{
    
    
	  "id":130000,
	  "name":"河北省",
	  "subs":[
	      {
    
    
	          "id":130100,
	          "name":"石家庄市"
	      },
	      ......
	  ]
	}
}

3、后端view实现

  • 获取参数
  • 如果参数不为空,表示查询市或县信息
    • 通过Areas类,获取id=省id的省信息
    • 通过Areas类,获取parent__id=省id的市数据集
    • 循环读取数据集,生成省份信息列表
      • 市份信息字典:{“id”:市份id,“name”:市份名称}
    • 生成subs_data数据
    • 返回前端json数据
      在这里插入图片描述

六、县列表获取

1、前端实现

  • 选择的市信息发生变化后,将市id发向后端发起县查询
    在这里插入图片描述

2、接口设计和定义

2.1 请求方式:

选项 方案
请求方法 get
请求地址 /areas/

2.2 请求参数 :

参数名 类型 是否必传 说明
area_id string 市id

2.3 响应结果 : json

响应结果 响应内容
code 状态码
errmsg 错误信息
sub_data 字典
  • 县数据JSON
{
    
    
	"code": "0", 
	"errmsg": "OK", 
	"sub_data": {
    
    
		"id": 130100, 
		"name": "石家庄市", 
		"subs": [
			{
    
    
				"id": 130102, 
				"name": "长安区"}, 
			},
			......
		]
	}
}

3、后端view实现

  • 获取参数
  • 如果参数不为空,表示查询市或县信息
    • 通过Areas类,获取id=市id的市信息
    • 通过Areas类,获取parent__id=市id的县数据集
    • 循环读取数据集,生成县份信息列表
      • 县份信息字典:{“id”:县id,“name”:县名称}
    • 生成subs_data数据
    • 返回前端json数据
  • 代码与市代码复用

七、代码优化

  • 省市县数据基本不会发生变化,可以放到缓存中保存起来,提高mysql数据读取次数,提高响应速度
    • 本次保存在CACHES的默认区域
  • 异常捕获及处理
  • 将省市县数据过期时间参数化

1、说明

  • 获取参数
  • 判断是参数是否为空
    • 如果参数为空,即获取省份信息
    • 如果参数不为空,即根据area_id获取市、县信息
  • 获取省份信息:
  • 从CACHES中读取 province_model_list
  • 如果province_model_list为空,表求无省份信息或省份信息失效,需要从mysql数据库中读取
  • 因需要访问数据库,可能存在读取失败或异常错误,需要异常捕获,
    • 通过Areas类,获取parent为空的数据集
    • 循环读取数据集,生成省份信息列表
    • 将省信息份保存到CACHES中
    • 此处不用返回,因province_model_list存在,也需要返回
  • 如果发生异常,保存日志,返回含错误信息的JSON
  • 返回包含省信息的JSON
  • 获取市、县信息
    • 从CACHES中读取 sub_data+area_id信息
    • 如果sub_data+area_id信息为空,表求无市、县信息或市、县信息失效,需要从mysql数据库中读取
    • 因需要访问数据库,可能存在读取失败或异常错误,需要异常捕获,
      • 通过Areas类,获取id=省id的省信息
      • 通过Areas类,获取parent__id=省id的市数据集
      • 循环读取数据集,生成省份信息列表
      • 生成subs_data数据
      • 将市、县信息份保存到CACHES中: sub_data+area_id
    • 如果发生异常,保存日志,返回含错误信息的JSON
    • 返回包含省信息的JSON

2、 优化后的代码

logger = logging.getLogger('django')

class AreasView(View):
   """省市区三级联动"""

   def get(self,request):
       # 判断当前是要查询省份还是市区数据
       area_id=request.GET.get("area_id")
       if not area_id:
           province_list = cache.get('province_list')

           if not province_list:
               # 查询省级数据
               try:
                   province_model_list = Area.objects.filter(parent__isnull=True)  # parent 是外键  parent__null 判断此字段是否为空
                   # print(type(province_model_list),province_model_list)
                   """
                   # 返回省份json数据格式
                   {
                     "code":"0",
                     "errmsg":"OK",
                     "province_list":[
                         {
                             "id":110000,
                             "name":"北京市"
                         },
                         {
                             "id":120000,
                             "name":"天津市"
                         }
                     ]
                   }
                   """
                   province_list=[]

                   for province_model in province_model_list:
                       province_dict={
    
    
                           "id":province_model.id,
                           "name":province_model.name,
                       }
                       province_list.append(province_dict)

                   # 将数据保存到Redis中 默认的
                   # cache.set("province_list", province_list, 3600)
                   cache.set("province_list", province_list, AREA_REDIS_EXPIRES)
               except Exception as e:
                   logger.error(e)
                   return http.JsonResponse({
    
    "code": RETCODE.DBERR, "errmsg": "查询省份数据错误"})
               # return http.JsonResponse({"code": RETCODE.OK, "errmsg": "OK", "province_list": province_list})
           # else:
           return http.JsonResponse({
    
    "code": RETCODE.OK, "errmsg": "OK", "province_list": province_list})
       else:
           # 查询城市或者区域的数据
           sub_data = cache.get("sub_data_" + area_id)
           if not sub_data:
               """
               # 返回市、县json数据格式
                   {
                       "code":"0",
                       "errmsg":"OK",
                       "sub_data":{
                           "id":130000,
                           "name":"河北省",
                           "subs":[
                             {
                                 "id":130100,
                                 "name":"石家庄市"
                             },
                             ......
                           ]
                       }
                   }
                   或
                   {
                       "code": "0", 
                       "errmsg": "OK", 
                       "sub_data": {
                           "id": 130100, 
                           "name": "石家庄市", 
                           "subs": [
                               {
                                   "id": 130102, 
                                   "name": "长安区"}, 
                               },
                               ......
                           ]
                       }
                   }
               """
               try:
                   parent_model=Area.objects.get(id=area_id)
                   sub_model_list = Area.objects.filter(parent__id=area_id)  # parent是Area的外键

                   subs=[]
                   for sub_model in sub_model_list:
                       sub_dict={
    
    
                           "id":sub_model.id,
                           "name":sub_model.name,
                       }
                       subs.append(sub_dict)
                   sub_data={
    
    
                       "id":parent_model.id,
                       "name":parent_model.name,
                       "subs":subs,
                   }
                   # cache.set("sub_data_" + area_id, sub_data, 3600)
                   cache.set("sub_data_" + area_id, sub_data, AREA_REDIS_EXPIRES)
               except Exception as e:
                   logger.error(e)
                   return http.JsonResponse({
    
    "code": RETCODE.DBERR, "errmsg": "查询城市或区县数据错误"})

           return http.JsonResponse({
    
    "code": RETCODE.OK, "errmsg": "OK", "sub_data": sub_data})


猜你喜欢

转载自blog.csdn.net/laoluobo76/article/details/114274170