地址模块的模型
由于在测试项目中并没有地址模块的html,就从别的地方弄了一个没有CSS样式的地址html坐下测试,大家将就一下
地址模块逻辑
我们这个地址模块中,主要是数据的调取,然后把数据存储到数据库中,所以存储的逻辑其实还是蛮简单的,不过我们的数据库中的逻辑就没这么简单了。
地址其实相当多,而且规格划分也非常多,我们不可能省一个表,然后每个省都给一个市表,每个市表都给一个区表,这不太现实。其实现在绝大多数地方都是用的内联表,比如省可以外键为空,市的外键是省,区的外键是市,这样查询省的时候就可以查询到省下的市,查询市就可以查询到区。
那么我们现在就要实现这个查询
建立一个查询表格将来用来查询地址
我新建一个地址模块来写查询逻辑
新建模块areas
setting中注册
INSTALLED_APPS = [
'areas.apps.AreasConfig',
]
转到models.py中进行表结构的编译
class Areas(models.Model):
name=models.CharField(max_length=50,verbose_name='location')
pid=models.ForeignKey('self',on_delete=models.SET_NULL,related_name='addinfos',null=True)
class Meta:
db_table='areas'
def __str__(self):
return self.name
简单的几行代码,因为其实存储的时候地址只是单单一张表,而且每个地址都是由id,名字和一个foreign键组成,所以存储表结构并不难。这时候先迁移一次,创建这个地址表。
在创建前,我们之前用的表是没有设置UTF8中文可写的,所以这里可能不得不把数据库删了重新迁移,这是我的锅
重新创建以后不要忘记迁移和重新注册用户,以及之前的管理员用户。因为管理员用户也存在了之前的AbstractUser表中。
现在我们可以在网上找一个省市区表,然后将文件导入到mysql中。
sql的导入在很多地方有比我详细的介绍,我就不说了,我自己是用了骚操作,就是把sql文件中内容复制粘贴进表里,也是可以的
地址表的形式
复制粘贴后是这样的,你需要自己打最后一个回车就可以了,其他都自动导入
现在表就导入完毕了。
完成了这些以后,我们需要用serializer和view来实现一下查询
这里我稍微理一下思路。省我们需要在用户进入以后就列出来,方法为List然后列出省以后用户会查询特定的省,这就是retrieve动作了。特定省中由于外键和他所有的市链接,这里我们就把所有的市拿出来,然后用户又会选定特定的市。同样的和之前一样,这样我们在视图里面就需要实现Retrive方法,但是三级视图没有同时实现这两个办法的(当然两个动作都是由GET方法衍生出来的,所以在同一个视图不是很好将同一个方法变成不同的动作,所以就没有这种视图了),这里就要引入视图集的概念了。
视图集
视图集官方文档
视图集相对于视图来说,并没有指定method,比如get,post,put这些,但他指定了动作,比如List(),Retrieve()这些。这些方法我们之前也是在Mixin中看到过,但是视图集相对来说可以在一个函数中实现多种动作,所以这里我们使用了视图集。
其实还是比较好理解的,我们前端发送的应该都是get请求,但是这个get请求是会带参数访问的,而在逻辑上,全选和选单个的方法又是不一样的,所以这里就用了视图集来写。
视图集的url需要设置route,因为一个视图集可以做出好几个视图。
完成视图集
讲了这么多,现在就拿来用用吧
views
from django.shortcuts import render
from rest_framework.viewsets import ReadOnlyModelViewSet
from . import models
from . import serializers
# Create your views here.
class AreasViewSet(ReadOnlyModelViewSet):
def get_queryset(self):
if self.action=='list': #列出省
return models.Areas.objects.filter(pid=None)
else: #选择省或市字段
return models.Areas.objects.all()
def get_serializer_class(self):
if self.action=='list': #获得不带pid即addinfos的省
return serializers.AreaInfoSerializer
else: #获得带pid的省或市,带addinfos列出下一级地区的id和name
return serializers.NextAreasInfoSeria
serializers
from . import models
from rest_framework import serializers
class AreaInfoSerializer(serializers.ModelSerializer):
class Meta:
model=models.Areas
fields=('id','name')
class NextAreasInfoSeria(serializers.ModelSerializer):
嵌套serializer查询外键具体内容,如果不添加,只输出外联表的外键id
addinfos=AreaInfoSerializer(many=True,read_only=True)
class Meta:
model=models.Areas
fields=('id','name','addinfos')
配置urls
模块内的urls
from .views import AreasViewSet
from rest_framework.routers import DefaultRouter
urlpatterns = [
]
router=DefaultRouter()
router.register('areas',AreasViewSet,base_name='areas')
urlpatterns +=router.urls
主urls
from django.conf.urls import url,include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'',include('user.urls')),
url(r'areas/',include('areas.urls')),
]
为了解释上面的代码我们ctrl-B进入ReadonlyModelViewset视图的retrieve方法实现,可以看见这里我们只要使用了retrieve我们就已经可以获取到request需要的字段了,但是为什么我们在上面retrieve的实现中加上了object.all()这个方法呢?其实这个字段只是用来输出所有符合查询的字段,也就是其他都没法用,所以就直接这样输出了,其实在retrieve中早就已经通过serializer查询到了我们指定的字段了
先开启后端服务器,根据上面的url找到我们的后端中的视图
这里GET方法,因为并没有指定字段,现在我们查询特定字段,在url后传一个参数给后端
查询出来的样子是这样的,这是因为有一个迭代的serializer,查询官方文档中的serializer嵌套可以知道这是专门为了用来查询带关系的关联表处理方式,为了让关联数据也能显示出来。
drf-extensions
省市区的数据将会被多次访问,但是本身数据库中数据变化不大,这里就可以添加缓存来减少查询次数
安装
进入我们的虚拟环境键入
pip install drf-extensions
给请求添加缓存
装饰器
我们可以给请求加一个装饰器
@cache_response(timeout=60*60, cache='default')
def get(self, request, token):
timeout 缓存时间,单位秒
cache 缓存使用的Django缓存后端即选择的是redis中default数据库
继承
drf-extensions扩展对于缓存提供了三个扩展类:
ListCacheResponseMixin
用于缓存返回列表数据的视图,与ListModelMixin扩展类配合使用,实际是为list方法添加了cache_response装饰器
RetrieveCacheResponseMixin
用于缓存返回单一数据的视图,与RetrieveModelMixin扩展类配合使用,实际是为retrieve方法添加了cache_response装饰器
CacheResponseMixin
为视图集同时补充List和Retrieve两种缓存,与ListModelMixin和RetrieveModelMixin一起配合使用。
我们只需要给我们这个带List和Retrieve两种动作的视图集添加扩展就可以了
from rest_framework_extensions.cache.mixins import CacheResponseMixin
# Create your views here.
class AreasViewSet(ReadOnlyModelViewSet,CacheResponseMixin):