省市区地址缓存

area.sql准备数据

首先数据库设计分析:

设计成自关联的一张表

字段:id name parent_id

省:parent_id = null

市: parent_id = 省的id

区(县):parent_id = 市的id

需求:

刷新页面时,显示全部的省份信息

选择省时,显示所有属于该省的市的信息

选择市时,显示所有属于该市的区(县)信息

1.创建一个模型类

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

    class Meta:
        db_table = 'tb_areas'
        verbose_name = '行政区划'
        verbose_name_plural = '行政区划'

    def __str__(self):
        return self.name

数据库迁移:

python manage.py makemigrations
python manage.py migrate

 2.导入数据

1.直接在数据库中粘贴所有area.sql文件中的内容

2.在终端中source导入area.sql文件(需要注意area.sql文件所在的位置)

3.脚本导入

         在项目中创建一个script文件夹

         在文件夹中创建一个my_area.sh文件

         在my_area.sh添加内容

#!/usr/bin/env bash

mysql -h127.0.0.1 -uusername -ppassword databasename < ./areas.sql

         修改文件的执行权限: 

chmod +x my_area.sh

在Teminal中运行

./my_area.sh

3.代码实现 

后端接口设计

1)请求省份数据

请求方式 GET /areas/infos/

返回数据

返回值 类型 是否必传 说明
id int 省份id
name str 省份名称

2)请求城市或区县数据

请求方式 GET/areas/infos/(?P<pk>\d+)/

请求参数

参数 类型 是否必传 说明
pk int 上级区划id(省份id用于获取城市数据,或城市id用于获取区县数据)

返回数据

返回值 类型 是否必传 说明
id int 上级区划id(省份id或城市id)
name str 上级区划的名称
subs list[] 下属所有区划信息

分析接口文档:

可以使用2个视图类来处理,也可以使用viewset来处理

可以看到这里都是get请求,我们可以使用ReadOnlyModelViewSet

由于省的查询集(parent= null)和市区的查询集null(parent=上一级id)不同,所以需要重写get_queryset方法

class AreasViewSet(ReadOnlyModelViewSet):
    pagination_class = None  # 区划信息不分页

    def get_queryset(self):
        """
        提供数据集
        """
        if self.action == 'list':
            return Area.objects.filter(parent=None)
        else:
            return Area.objects.all()

    serializer_class = AreaSerializer

序列化器:

class AreaSerializer(serializers.ModelSerializer):
    """
    行政区划信息序列化器
    """

    class Meta:
        model = Area
        fields = ['id', 'name', 'parent_id']

为什么市区的可以直接 return Area.objects.all()呢?

 ReadOnlyModelViewSet继承了mixins.RetrieveModelMixin

RetrieveModelMixin里面有instance = self.get_object(),根据返回的PK值获取到对象

get_object()根据前端传过来的PK值获取到具体的对象

这里的URL路径是由router生成的,所以我们要了解路由Router的具体生成url的过程

from django.conf.urls import url,include
from rest_framework.routers import DefaultRouter
from .views import AreasViewSet

router = DefaultRouter()
router.register(r'infos',AreasViewSet,base_name='area')

urlpatterns = [
    # url(r'^',include(router.urls))
]

#添加省市区信息查询路由
urlpatterns += router.urls

 

用postman测试

这时候添加在路径加入省的ID只能获取到省的信息,要怎么才能获取到市的信息呢?

我们知道序列化器中使用关联对象的序列化器

重写序列化器:

class AreaSerializer(serializers.ModelSerializer):
    """
    行政区划信息序列化器
    """

    class Meta:
        model = Area
        fields = ['id', 'name', 'parent_id']

class SubAreaSerializer(serializers.ModelSerializer):
    area_set = AreaSerializer(many=True,read_only=True)
    class Meta:
        model = Area
        fields = ['area_set']

重写视图函数:

class AreasViewSet(CacheResponseMixin,ReadOnlyModelViewSet):
    pagination_class = None  # 区划信息不分页

    def get_queryset(self):
        """
        提供数据集
        """
        if self.action == 'list':
            return Area.objects.filter(parent=None)
        else:
            return Area.objects.all()

    # serializer_class = AreaSerializer
    def get_serializer_class(self):
        if self.action == 'list':
            return AreaSerializer
        else:
            return SubAreaSerializer

当获取省的信息时,调用AreaSerializer

当获取市,区信息时,调用SubAreaSerializer

前端代码:

 <form>
                    <div class="form_group">
                        <label>*收货人:</label>
                        <input v-model="form_address.receiver" @blur="check_receiver" type="text" name="">
                        <span v-show="error_receiver" class="error_tip">请填写收件人</span>
                    </div>
                    <div class="form_group">
                        <label>*所在地区:</label>
                        <select v-model="form_address.province_id">
                            <option v-for="province in provinces" v-bind:value="province.id">{{ province.name }}</option>
                        </select>
                        <select v-model="form_address.city_id">
                            <option v-for="city in cities" v-bind:value="city.id">{{ city.name }}</option>
                        </select>
                        <select v-model="form_address.district_id">
                            <option v-for="district in districts" v-bind:value="district.id">{{ district.name }}</option>
                        </select>
                    </div>
                    <div class="form_group">
                        <label>*详细地址:</label>
                        <input v-model="form_address.place" @blur="check_place" type="text" name="">
                        <span v-show="error_place" class="error_tip">请填写地址信息</span>
                    </div>
                    <div class="form_group">
                        <label>*手机:</label>
                        <input v-model="form_address.mobile" @blur="check_mobile" type="text" name="">
                        <span v-show="error_mobile" class="error_tip">手机信息有误</span>
                    </div>
                    <div class="form_group">
                        <label>固定电话:</label>
                        <input v-model="form_address.tel" type="text" name="">
                    </div>
                    <div class="form_group">
                        <label>邮箱:</label>
                        <input v-model="form_address.email" @blur="check_email" type="text" name="">
                        <span v-show="error_email" class="error_tip">邮箱信息有误</span>
                    </div>
                    <input @click="save_address" type="button" name="" value="保 存" class="info_submit">
                    <input @click="is_show_edit=false" type="reset" name="" value="取 消" class="info_submit info_reset">
                </form>

JS

watch: {
        'form_address.province_id': function(){
            if (this.form_address.province_id) {
                axios.get(this.host + '/areas/infos/'+ this.form_address.province_id + '/', {
                        responseType: 'json'
                    })
                    .then(response => {
                        this.cities = response.data.area_set;
                    })
                    .catch(error => {
                        console.log(error.response.data);
                        this.cities = [];
                    });
            }
        },
        'form_address.city_id': function(){
            if (this.form_address.city_id){
                axios.get(this.host + '/areas/infos/'+ this.form_address.city_id + '/', {
                        responseType: 'json'
                    })
                    .then(response => {
                        this.districts = response.data.area_set;
                    })
                    .catch(error => {
                        console.log(error.response.data);
                        this.districts = [];
                    });
            }
        }
    },

postman测试:

浏览器测试:

 

4.使用缓存

省市区的数据是经常被用户查询使用的,而且数据基本不变化,所以我们可以将省市区数据进行缓存处理,减少数据库的查询次数。

在Django REST framework中使用缓存,可以通过drf-extensions扩展来实现。

安装:

pip install drf-extensions

为省市区视图添加缓存

from rest_framework_extensions.cache.mixins import CacheResponseMixin
# Create your views here.

class AreaViewSet(CacheResponseMixin,ReadOnlyModelViewSet):
    """
    行政区划信息
    list: GET /areas/
    retrieve: GET /areas/(?P<pk>\d+)/
    """

 在setting里添加配置信息

# DRF扩展
REST_FRAMEWORK_EXTENSIONS = {
    # 缓存时间
    'DEFAULT_CACHE_RESPONSE_TIMEOUT': 60 * 60,
    # 缓存存储
    'DEFAULT_USE_CACHE': 'default',
}

猜你喜欢

转载自blog.csdn.net/Spencer_q/article/details/82109998