三、收货地址
2. 准备省市区模型和数据
4. Vue渲染省市区数据
用户地址的主要业务逻辑有:
- 展示省市区数据
- 用户地址的增删改查处理
- 设置默认地址
- 设置地址标题
提示:
- 省市区数据是在收货地址界面展示的,所以我们先渲染出收货地址界面。
- 收货地址界面中基础的交互已经提前实现。
class AddressView(LoginRequiredMixin, View):
"""用户收货地址"""
def get(self, request):
"""提供收货地址界面"""
return render(request, 'user_center_site.html')
把.html和.js文件复制到响应的位置。
urls.py
# 展示用户收货地址
url(r'^addresses/$', views.AddressView.as_view(), name='address'),


新建子应用areas专门管理省市区三级联动
class Area(models.Model):
"""省市区"""
name = models.CharField(max_length=20, verbose_name='名称')
parent = models.ForeignKey('self', on_delete=models.SET_NULL, related_name='subs', null=True, blank=True, verbose_name='上级行政区划')
class Meta:
db_table = 'tb_areas'
verbose_name = '省市区'
verbose_name_plural = '省市区'
def __str__(self):
return self.name
模型说明:
- 自关联字段的外键指向自身,所以
models.ForeignKey('self')
- 使用
related_name
指明父级查询子级数据的语法- 默认
Area模型类对象.area_set
语法
- 默认
related_name='subs'
- 现在
Area模型类对象.subs
语法
- 现在
导入省市区数据:
mysql -h数据库ip地址 -u数据库用户名 -p数据库密码 数据库 < areas.sql
mysql -h127.0.0.1 -uroot -pmysql meiduo_mall < areas.sql
将areas.sql文件导入到数据库中
3. 查询省市区数据1.请求方式
选项方案请求方法GET请求地址/areas/总路由:
# areas
url(r'^', include('areas.urls')),
子路由:
from django.conf.urls import url
from . import views
urlpatterns = [
# 省市区三级联动
url(r'^areas/$', views.AreasView.as_view()),
]
2.请求参数:查询参数
- 如果前端没有传入
area_id
,表示用户需要省份数据 - 如果前端传入了
area_id
,表示用户需要市或区数据
3.响应结果:JSON
-
省份数据
{ "code":"0", "errmsg":"OK", "province_list":[ { "id":110000, "name":"北京市" }, { "id":120000, "name":"天津市" }, { "id":130000, "name":"河北省" }, ...... ] }
-
市或区数据
{ "code":"0", "errmsg":"OK", "sub_data":{ "id":130000, "name":"河北省", "subs":[ { "id":130100, "name":"石家庄市" }, ...... ] } }
4.查询省市区数据后端逻辑实现
- 如果前端没有传入
area_id
,表示用户需要省份数据 - 如果前端传入了
area_id
,表示用户需要市或区数据
from django.shortcuts import render
from django.views import View
from .models import Area
from django import http
from meiduo_mall.utils.response_code import RETCODE
import logging
# 创建日志输出器
logger = logging.getLogger('django')
# Create your views here.
class AreasView(View):
"""省市区数据"""
def get(self, request):
"""提供省市区数据"""
area_id = request.GET.get('area_id')
if not area_id:
# 提供省份数据
try:
# 查询省份数据
province_model_list = Area.objects.filter(parent__isnull=True)
# 序列化省级数据
province_list = []
for province_model in province_model_list:
province_list.append({'id': province_model.id, 'name': province_model.name})
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:
# 提供市或区数据
try:
parent_model = Area.objects.get(id=area_id) # 查询市或区的父级
sub_model_list = parent_model.subs.all()
# 序列化市或区数据 , 将子级模型列表转成字典列表
sub_list = []
for sub_model in sub_model_list:
sub_list.append({'id': sub_model.id, 'name': sub_model.name})
# 构造子集json数据
sub_data = {
'id': parent_model.id, # 父级pk
'name': parent_model.name, # 父级name
'subs': sub_list # 父级的子集
}
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})


1.user_center_site.js
中
mounted() {
// 获取省份数据
this.get_provinces();
},
// 获取省份数据
get_provinces(){
let url = '/areas/';
axios.get(url, {
responseType: 'json'
})
.then(response => {
if (response.data.code == '0') {
this.provinces = response.data.province_list;
} else {
console.log(response.data);
this.provinces = [];
}
})
.catch(error => {
console.log(error.response);
this.provinces = [];
})
},
watch: {
// 监听到省份id变化
'form_address.province_id': function(){
if (this.form_address.province_id) {
let url = '/areas/?area_id=' + this.form_address.province_id;
axios.get(url, {
responseType: 'json'
})
.then(response => {
if (response.data.code == '0') {
this.cities = response.data.sub_data.subs;
} else {
console.log(response.data);
this.cities = [];
}
})
.catch(error => {
console.log(error.response);
this.cities = [];
})
}
},
// 监听到城市id变化
'form_address.city_id': function(){
if (this.form_address.city_id){
let url = '/areas/?area_id='+ this.form_address.city_id;
axios.get(url, {
responseType: 'json'
})
.then(response => {
if (response.data.code == '0') {
this.districts = response.data.sub_data.subs;
} else {
console.log(response.data);
this.districts = [];
}
})
.catch(error => {
console.log(error.response);
this.districts = [];
})
}
}
},
2.user_center_site.html
中
*所在地区:
[[ province.name ]]
[[ city.name ]]
[[ district.name ]]
5. 缓存省市区数据
提示:
- 省市区数据是我们动态查询的结果。
- 但是省市区数据不是频繁变化的数据,所以没有必要每次都重新查询。
- 所以我们可以选择对省市区数据进行缓存处理。
1.缓存工具
from django.core.cache import cache
- 存储缓存数据:
cache.set('key', 内容, 有效期)
- 读取缓存数据:
cache.get('key')
- 删除缓存数据:
cache.delete('key')
- 注意:存储进去和读取出来的数据类型相同,所以读取出来后可以直接使用。
2.缓存逻辑
3.缓存逻辑实现
- 省份缓存数据
cache.set('province_list', province_list, 3600)
- 市或区缓存数据
cache.set('sub_area_' + area_id, sub_data, 3600)
from django.shortcuts import render
from django.views import View
from .models import Area
from django import http
from meiduo_mall.utils.response_code import RETCODE
from django.core.cache import cache
import logging
# 创建日志输出器
logger = logging.getLogger('django')
# Create your views here.
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)
# 序列化省级数据
province_list = []
for province_model in province_model_list:
province_list.append({'id': province_model.id, 'name': province_model.name})
except Exception as e:
logger.error(e)
return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '省份数据错误'})
# 存储省份缓存数据
cache.set('province_list', province_list, 3600)
# 响应省份数据
return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'province_list': province_list})
else:
# 读取市或区缓存数据
sub_data = cache.get('sub_area_' + area_id)
if not sub_data:
# 提供市或区数据
try:
parent_model = Area.objects.get(id=area_id) # 查询市或区的父级
sub_model_list = parent_model.subs.all()
# 序列化市或区数据 , 将子级模型列表转成字典列表
sub_list = []
for sub_model in sub_model_list:
sub_list.append({'id': sub_model.id, 'name': sub_model.name})
# 构造子集json数据
sub_data = {
'id': parent_model.id, # 父级pk
'name': parent_model.name, # 父级name
'subs': sub_list # 父级的子集
}
except Exception as e:
logger.error(e)
return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '城市或区数据错误'})
# 存储市或区缓存数据
cache.set('sub_area_' + area_id, sub_data, 3600)
# 响应市或区数据
return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'sub_data': sub_data})