Django DRF
REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征性状态转移)。 它首次出现在2000年Roy Fielding的博士论文中。
RESTful是一种定义Web API接口的设计风格,尤其适用于前后端分离的应用模式中。
这种风格的理念认为后端开发任务就是提供数据的,对外提供的是数据资源的访问接口,所以在定义接口时,客户端访问的URL路径就表示这种要操作的数据资源。
事实上,我们可以使用任何一个框架都可以实现符合restful规范的API接口。
url链接一般都采用https协议进行传输
注:采用https协议,可以提高数据交互过程中的安全性
用api关键字标识接口url:
注:看到api字眼,就代表该请求url链接是完成前后台数据交互的
在url链接中标识数据版本
注:url链接中的v1、v2就是不同数据版本的体现(只有在一种数据资源有多版本情况下)
接口一般都是完成前后台数据的交互,交互的数据我们称之为资源
注:一般提倡用资源的复数形式,在url链接中奖励不要出现操作资源的动词,错误示范:https://api.baidu.com/delete-user
特殊的接口可以出现动词,因为这些接口一般没有一个明确的资源,或是动词就是接口的核心含义
{
error: "无权限操作"
}
GET /collection:返回资源对象的列表(数组)
GET /collection/resource:返回单个资源对象
POST /collection:返回新生成的资源对象
PUT /collection/resource:返回完整的资源对象
PATCH /collection/resource:返回完整的资源对象
DELETE /collection/resource:返回一个空文档
# Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么
{
"status": 0,
"msg": "ok",
"results":[
{
"name":"肯德基(罗餐厅)",
"img": "https://image.baidu.com/kfc/001.png"
}
...
]
}
比较好的接口返回
# 响应数据要有状态码、状态信息以及数据本身
{
"status": 0,
"msg": "ok",
"results":[
{
"name":"肯德基(罗餐厅)",
"location":{
"lat":31.415354,
"lng":121.357339
},
"address":"月罗路2380号",
"province":"上海市",
"city":"上海市",
"area":"宝山区",
"street_id":"339ed41ae1d6dc320a5cb37c",
"telephone":"(021)56761006",
"detail":1,
"uid":"339ed41ae1d6dc320a5cb37c"
}
...
]
}
作用:
1. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串
2. 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型
3. 反序列化,完成数据校验功能
from rest_framework import serializers
# 声明序列化器,所有的序列化器都要直接或者间接继承于 Serializer
class StudentSerializer(serializers.Serializer):
"""学生信息序列化器"""
# 1. 需要进行数据转换的字段
id = serializers.IntegerField()
name = serializers.CharField()
age = serializers.IntegerField()
sex = serializers.BooleanField()
description = serializers.CharField()
# 2. 如果序列化器继承的是ModelSerializer,则需要声明调用的模型信息
# 3. 验证代码
# 4. 编写添加和更新模型的代码
注意:serializer不是只能为数据库模型类定义,也可以为非数据库模型类的数据定义。serializer是独立于数据库之外的存在。
ModelsSerializer与常规的Serializer相同,但提供了额外功能:
基于模型类自动生成一系列字段
基于模型类自动为Serializer生成Validators,校验器
包含默认的create()和update()实现。自动操作表,创建和更新表对象
1.我们创建一个BookInfoSerializer
class BookInfoSerializer(serializers.ModelSerializer):
"""图书数据序列化器"""
class Meta:
model = BookInfo
fields = ‘__all__‘
model指明参照哪个模型类
fields指明为模型类的哪些字段生成
2.指定字段
2.1)使用字段来明确字段,__all__表名包含所有字段,也可以写明具体哪些字段,如
class BookInfoSerializer(serializers.ModelSerializer):
"""图书数据序列化器"""
class Meta:
model = BookInfo
fields = (‘id‘, ‘btitle‘, ‘bpub_date‘)
#fields作用详解:
fields内可以填入的不仅仅只有model的字段,还可以是model中定义的方法,序列化类中定义的方法和字段
-表模型对应的字段:直接序列化字段的内容,若是外键字段返回一个对象,需要在表模型中定义函数
-model中定义的方法:序列化方法的结果,一般用来定制外键字段输出的信息,
-
-
注意:fields中不在model表中的字段都需要设置为只读!
2.2)使用排除可以明确排除掉哪些字段
class BookInfoSerializer(serializers.ModelSerializer):
"""图书数据序列化器"""
class Meta:
model = BookInfo
exclude = (‘image‘,)
2.3)默认ModelSerializer使用主键作为关联字段,但是我们可以使用depth来简单的生成嵌套表示,depth应该是整数,表明嵌套的层级数量。
class HeroInfoSerializer2(serializers.ModelSerializer):
class Meta:
model = HeroInfo
fields = ‘__all__‘
depth = 1
2.4)显示指明字段,如:
class HeroInfoSerializer(serializers.ModelSerializer):
hbook = BookInfoSerializer()
class Meta:
model = HeroInfo
fields = (‘id‘, ‘hname‘, ‘hgender‘, ‘hcomment‘, ‘hbook‘)
2.5)申明只读字段:read_only_fields
class BookInfoSerializer(serializers.ModelSerializer):
"""图书数据序列化器"""
class Meta:
model = BookInfo
fields = (‘id‘, ‘btitle‘, ‘bpub_date‘, ‘bread‘, ‘bcomment‘)
read_only_fields = (‘id‘, ‘bread‘, ‘bcomment‘)
3.添加额外参数 extra_kwargs
我们可以使用extra_kwargs参数为ModelSerializer添加或修改原有的选项参数
class BookInfoSerializer(serializers.ModelSerializer):
"""图书数据序列化器"""
class Meta:
model = BookInfo
fields = (‘id‘, ‘btitle‘, ‘bpub_date‘, ‘bread‘, ‘bcomment‘)
extra_kwargs = {
‘bread‘: {‘min_value‘: 0, ‘required‘: True},
‘bcomment‘: {‘min_value‘: 0, ‘required‘: True},
}
序列化器的使用分两个阶段:
1) 先查询出一个对象
from students.models import Student
student = Student.objects.get(id=3)
2) 构造序列化器对象
from .serializers import StudentSerializer
serializer = StudentSerializer(instance=student)
3)获取序列化数据
通过data属性可以获取序列化后的数据,是一个字典
serializer.data
# {‘id‘: 4, ‘name‘: ‘小张‘, ‘age‘: 18, ‘sex‘: True, ‘description‘: ‘猴赛雷‘}
说明:
1)用于序列化时,只传一个instance参数,默认第一个参数
2)用于反序列化时,只传入data参数
4)如果要被序列化的是包含多条数据的查询集QuerySet,可以通过添加many=True参数补充说明
5)除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据,如
serializer = AccountSerializer(account, context={‘request‘: request})
通过context参数附加的数据,可以通过Serializer对象的context属性获取,这样一些与请求有关的逻辑判断可以再序列化类里操作了
视图代码:
from django.views import View
from students.models import Student
from .serializers import StudentSerializer
from django.http.response import JsonResponse
class StudentView(View):
"""使用序列化器序列化转换单个模型数据"""
def get(self,request,pk):
# 获取数据
student = Student.objects.get(pk=pk)
# 数据转换[序列化过程]
serializer = StudentSerializer(instance=student)
print(serializer.data)
# 响应数据
return JsonResponse(serializer.data)
"""使用序列化器序列化转换多个模型数据"""
def get(self,request):
# 获取数据
student_list = Student.objects.all()
# 转换数据[序列化过程]
# 如果转换多个模型对象数据,则需要加上many=True
serializer = StudentSerializer(instance=student_list,many=True)
print( serializer.data ) # 序列化器转换后的数据
# 响应数据给客户端
# 返回的json数据,如果是列表,则需要声明safe=False
return JsonResponse(serializer.data,safe=False)
from rest_framework.request import Request
REST framework 传入视图的request对象不再是Django默认的HttpRequest对象,而是REST framework提供的扩展了HttpRequest类的Request类的对象。
REST framework 提供了Parser解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON、表单等)将请求数据进行parse解析,解析为类字典[QueryDict]对象保存到Request对象中。
Request对象的数据是自动根据前端发送数据的格式进行解析之后的结果。
无论前端发送的哪种格式的数据,我们都可以以统一的方式读取数据。
1).data
request.data
返回解析之后的请求体数据。类似于Django中标准的request.POST
和 request.FILES
属性,但提供如下特性:
2).query_params
request.query_params
与Django标准的request.GET
相同,只是更换了更正确的名称而已。
3).请求头的数据
ip地址和token都在http的请求头里
request.META.get(‘HTTP_REMOTE_ADDR‘) 取IP地址
request.META.get(‘HTTP_AUTHORIZATION‘) 取token
补充:
Http请求版本号
from rest_framework.response import Response
REST framework提供了一个响应类Response
,使用该类构造响应对象时,响应的具体数据内容会被转换(render渲染)成符合前端需求的类型。
REST framework提供了Renderer
渲染器,用来根据请求头中的Accept
(接收数据类型声明)来自动转换响应数据到对应格式。如果前端请求中未进行Accept声明,则会采用默认方式处理响应数据,我们可以通过配置来修改默认响应格式。
可以在rest_framework.settings查找所有的drf默认配置项
REST_FRAMEWORK = {
‘DEFAULT_RENDERER_CLASSES‘: ( # 默认响应渲染类
‘rest_framework.renderers.JSONRenderer‘, # json渲染器
‘rest_framework.renderers.BrowsableAPIRenderer‘, # 浏览API渲染器
)
}
Response(data, status=None, template_name=None, headers=None, content_type=None)
data
数据不要是render处理之后的数据,只需传递python的内建类型数据即可,REST framework会使用renderer
渲染器处理data
。
data
不能是复杂结构的数据,如Django的模型类对象,对于这样的数据我们可以使用Serializer
序列化器序列化处理后(转为了Python字典类型)再传递给data
参数。
参数说明:
data
: 前段要展示的数据,def的data格式不友好status
: 状态码,给浏览器识别的,浏览器会更据改状态码做出反应,默认200;template_name
: 模板名称,如果使用HTMLRenderer
时需指明;headers
: 用于存放响应头信息的字典;content_type
: 响应数据的Content-Type,通常此参数无需传递,REST framework会根据前端所需类型数据来设置该参数。1).data
传给response对象的序列化后,但尚未render处理的数据
2).status_code
状态码的数字
3).content
经过render处理后的响应数据
为了方便设置状态码,REST framewrok在rest_framework.status
模块中提供了常用状态码常量。
1)信息告知 - 1xx
HTTP_100_CONTINUE
HTTP_101_SWITCHING_PROTOCOLS
2)成功 - 2xx
HTTP_200_OK
HTTP_201_CREATED
HTTP_202_ACCEPTED
HTTP_203_NON_AUTHORITATIVE_INFORMATION
HTTP_204_NO_CONTENT
HTTP_205_RESET_CONTENT
HTTP_206_PARTIAL_CONTENT
HTTP_207_MULTI_STATUS
3)重定向 - 3xx
HTTP_300_MULTIPLE_CHOICES
HTTP_301_MOVED_PERMANENTLY
HTTP_302_FOUND
HTTP_303_SEE_OTHER
HTTP_304_NOT_MODIFIED
HTTP_305_USE_PROXY
HTTP_306_RESERVED
HTTP_307_TEMPORARY_REDIRECT
4)客户端错误 - 4xx
HTTP_400_BAD_REQUEST
HTTP_401_UNAUTHORIZED
HTTP_402_PAYMENT_REQUIRED
HTTP_403_FORBIDDEN
HTTP_404_NOT_FOUND
HTTP_405_METHOD_NOT_ALLOWED
HTTP_406_NOT_ACCEPTABLE
HTTP_407_PROXY_AUTHENTICATION_REQUIRED
HTTP_408_REQUEST_TIMEOUT
HTTP_409_CONFLICT
HTTP_410_GONE
HTTP_411_LENGTH_REQUIRED
HTTP_412_PRECONDITION_FAILED
HTTP_413_REQUEST_ENTITY_TOO_LARGE
HTTP_414_REQUEST_URI_TOO_LONG
HTTP_415_UNSUPPORTED_MEDIA_TYPE
HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE
HTTP_417_EXPECTATION_FAILED
HTTP_422_UNPROCESSABLE_ENTITY
HTTP_423_LOCKED
HTTP_424_FAILED_DEPENDENCY
HTTP_428_PRECONDITION_REQUIRED
HTTP_429_TOO_MANY_REQUESTS
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE
HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS
5)服务器错误 - 5xx
HTTP_500_INTERNAL_SERVER_ERROR
HTTP_501_NOT_IMPLEMENTED
HTTP_502_BAD_GATEWAY
HTTP_503_SERVICE_UNAVAILABLE
HTTP_504_GATEWAY_TIMEOUT
HTTP_505_HTTP_VERSION_NOT_SUPPORTED
HTTP_507_INSUFFICIENT_STORAGE
HTTP_511_NETWORK_AUTHENTICATION_REQUIRED
class APIResponse(Response):
def __init__(self,code=100,msg=‘成功‘,data=None,status=None,headers=None,**kwargs):
dic = {‘code‘: code, ‘msg‘: msg}
if data:
dic = {‘code‘: code, ‘msg‘: msg,‘data‘:data}
dic.update(kwargs)
super().__init__(data=dic, status=status,headers=headers)
# 自己封装的参数,code,msg,data都会被放进一个字典中,然后将该字典打包传个drf的Response的data
# code是指自定义的状态码,为了说明用户的操作结果,浏览器不会根据它做出反应,是写在data里的
# status是服务器处理状态码,浏览器会根据该状态码做出反应,一般不需要手动填写
作用:
1,没权限等异常,程序内部异常,也输出json格式
2,记录日志
前后端分离的项目,如果出现异常(程序出现问题了),drf能处理的异常有限,交由Django处理的异常返回的不是json格式的字符串,我们需要拦截这些异常,并格式化异常的范厂长格式.
默认的异常处理是:
这是drf的settings的配置,若要更改默认配置需要在项目的settings里重新配置.
‘EXCEPTION_HANDLER‘: ‘rest_framework.views.exception_handler‘,
源码:
from rest_framework.views import exception_handler
def exception_handler(exc, context):
if isinstance(exc, Http404):
exc = exceptions.NotFound()
elif isinstance(exc, PermissionDenied):
exc = exceptions.PermissionDenied()
if isinstance(exc, exceptions.APIException):
headers = {}
if getattr(exc, ‘auth_header‘, None):
headers[‘WWW-Authenticate‘] = exc.auth_header
if getattr(exc, ‘wait‘, None):
headers[‘Retry-After‘] = ‘%d‘ % exc.wait
if isinstance(exc.detail, (list, dict)):
data = exc.detail
else:
data = {‘detail‘: exc.detail}
set_rollback()
return Response(data, status=exc.status_code, headers=headers)
return None
自定义异常
# 自定义异常处理的方法
from rest_framework.views import exception_handler
from rest_framework.response import APIResponse # 自定义包装的response
from rest_framework import status
def my_exception_handler(exc, context):
response=exception_handler(exc, context)
# 两种情况,一个是None,drf没有处理
#response对象,django处理了,但是处理的不符合咱们的要求
if not response: #drf没处理的情况,还可以更细力度的捕获异常
if isinstance(exc, ZeroDivisionError):
return APIResponse(data={‘status‘: 777, ‘msg‘: "除以0的错误" + str(exc)}, status=status.HTTP_400_BAD_REQUEST)
return APIResponse(data={‘status‘:999,‘msg‘:str(exc)},status=status.HTTP_400_BAD_REQUEST)
else: # drf处理的异常,我们包装一下格式
return APIResponse(data={‘status‘:888,‘msg‘:response.data.get(‘detail‘)},status=status.HTTP_400_BAD_REQUEST)
# 全局配置setting.py
‘EXCEPTION_HANDLER‘: ‘app01.app_auth.my_exception_handler‘,
返回与异常模板
from rest_framework.response import Response
from rest_framework.views import exception_handler
from rest_framework import status
# 自定义返回与异常捕获
class APIResponse(Response):
def __init__(self,code=100,msg=‘成功‘,data=None,status=None,headers=None,**kwargs):
dic = {‘code‘: code, ‘msg‘: msg}
if data:
dic = {‘code‘: code, ‘msg‘: msg,‘data‘:data}
dic.update(kwargs)
super().__init__(data=dic, status=status,headers=headers)
def my_exception_handler(exc, context):
"""
使用前先全局配置
"""
response = exception_handler(exc, context)
# drf没处理的情况,自己捕获异常并包装格式
if not response:
if isinstance(exc, ZeroDivisionError):
return APIResponse(data={‘status‘: 777, ‘msg‘: "除以0的错误" + str(exc)}, status=status.HTTP_400_BAD_REQUEST)
return APIResponse(data={‘status‘: 999, ‘msg‘: str(exc)}, status=status.HTTP_400_BAD_REQUEST)
else: # drf处理的异常,包装一下格式
return APIResponse(data={‘status‘: 888, ‘msg‘: response.data.get(‘detail‘)}, status=status.HTTP_400_BAD_REQUEST)
1)手动写orm语句
book_list=Book.objects.all()
2)手动调用序列化器
book_ser=BookSerializer(book_list,many=True)
3)手动写业务逻辑处理不同的请求
def get(self,request):
def post(self,request):
4,视图的类需要写两个,分别对应url的有名分组传参和不传参情况.
url(r‘^api/books2/$‘, views.Books2.as_view()),
url(r‘^api/book2/(?P<num>\d+)/‘, views.Book2.as_view())
class BookView(APIView):
def get(self,request):
book_list=Book.objects.all()
book_ser=BookSerializer(book_list,many=True)
return Response(book_ser.data)
def post(self,request):
book_ser = BookSerializer(data=request.data)
if book_ser.is_valid():
book_ser.save()
return Response(book_ser.data)
else:
return Response({‘status‘:101,‘msg‘:‘校验失败‘})
class BookDetailView(APIView):
def get(self, request,pk):
book = Book.objects.all().filter(pk=pk).first()
book_ser = BookSerializer(book)
return Response(book_ser.data)
def put(self, request,pk):
book = Book.objects.all().filter(pk=pk).first()
book_ser = BookSerializer(instance=book,data=request.data)
if book_ser.is_valid():
book_ser.save()
return Response(book_ser.data)
else:
return Response({‘status‘: 101, ‘msg‘: ‘校验失败‘})
def delete(self,request,pk):
ret=Book.objects.filter(pk=pk).delete()
return Response({‘status‘: 100, ‘msg‘: ‘删除成功‘})
简化步骤,在类里直接申明了操作哪张表,调用哪个序列化器:
queryset = models.Booklib.objects
serializer_class = BookModelSerializer
请求的函数调用申明的列表和序列化器会自动传入对象
book=self.get_queryset().filter(pk=num).first()
ser_obj=self.get_serializer(book)
下面分别对应5种方法的注意事项:
class Books2(GenericAPIView):
queryset = models.Booklib.objects.all()
serializer_class = BookModelSerializer
def get(self,request):
book=self.get_queryset()
book_ser=self.get_serializer(book,many=True)
return CommomResponse(data=book_ser.data,headers={‘key‘:‘value‘})
def post(self,request):#创建,修改了id为只读属性,创建时不需要id参数
book_ser=self.get_serializer(data=request.data)
if book_ser.is_valid():
book_ser.save()
return CommomResponse(data=book_ser.data)
else:
return CommomResponse(101,‘数据不和法‘,book_ser.errors)
class Book2(GenericAPIView):
queryset = models.Booklib.objects
serializer_class = ser.BookModelSerializer
def get(self,request,num):
book=self.get_queryset().filter(pk=num).first()
ser_obj=self.get_serializer(book)
return CommomResponse(data=ser_obj.data)
def delete(self,request,num):
book=self.get_queryset().filter(pk=num).delete()
return CommomResponse(msg=‘删除成功‘)
def put(self,request,num):
book=self.get_queryset().filter(pk=num).first()#只有一个值时一定有用.first()
ser_obj=self.get_serializer(book,request.data)
if ser_obj.is_valid():
ser_obj.save() #这是序列化器的save方法,不是queryset的
return CommomResponse(data=ser_obj.data)
else:
return CommomResponse(msg=‘数据不合法‘)
5个视图扩展类:直接继承object的类,这些类能自动能更具请求方式相应
ListModelMixin:获取所有数据
RetrieveModelMixin:获取一条数据
CreateModelMixin:创建
UpdateModelMixin:更新
DestroyModelMixin:删除
使用方法:在相应的请求函数下调用对应的函数,有参数的还得传参:
class Books3(GenericAPIView,ListModelMixin,CreateModelMixin):
queryset = models.Booklib.objects
serializer_class = ser.BookModelSerializer
def get(self,request):
return self.list(request)
def post(self,request):
return self.create(request)
class Book3(GenericAPIView,RetrieveModelMixin,DestroyModelMixin,UpdateModelMixin):
queryset = models.Booklib.objects
serializer_class = ser.BookModelSerializer
def get(self,request,pk):
return self.retrieve(request,pk)
def put(self,request,pk):
return self.update(request,pk)
def delete(self,request,pk):
return self.destroy(request,pk)
分别是:
5个基础组合类
CreateAPIView,ListAPIView,RetrieveAPIView,DestroyAPIView,UpdateAPIView
4个扩展组合类
ListCreateAPIView,RetrieveUpdateAPIView,RetrieveDestroyAPIView,RetrieveUpdateDestroyAPIView
这9个类集合了API类和ModelMixin类的优点,同时又更进一步,类体写了请求对应的操作,继承这个类之后只需要申明操作票那个表和指定那个序列化器.
使用方法:只需要这6行代码实现面的功能
class Books4(ListCreateAPIView):
queryset = models.Booklib.objects
#这里需要一个queryse对像所以要去到object
serializer_class = ser.BookModelSerializer
class Book4(RetrieveUpdateDestroyAPIView):
queryset = models.Booklib.objects
serializer_class = ser.BookModelSerializer
作用:只需要编写一个视图类来对应两个url
该类重写了的as_view功能匹配了请求的method和url视图层action,并且调用函数
注意:因为ViewSetMixin重写了as_view,查找as_view时会现去继承的第一父类查找,如果APIView在前则,不会执行重写的as_view导致功能失效.
当然这里的APIView也可以是GenericAPIView
class Book05(ViewSetMixin,APIView):
def get_one_book(self,request,pk):
obj_list = models.Booklib.objects.filter(pk=pk).first()
ser_book = ser.BookModelSerializer(obj_list)
return CommomResponse(data=ser_book.data)
def get_books(self,request):
obj_list = models.Booklib.objects.all()
ser_book=ser.BookModelSerializer(obj_list,many=True)
return CommomResponse(data=ser_book.data)
#路由层可以使用actions参数
url(r‘^api/books5/$‘, views.Book05.as_view(actions={‘get‘:‘get_books‘})),
url(r‘^api/book5/(?P<pk>\d+)/‘, views.Book05.as_view(actions={‘get‘:‘get_one_book‘})),
ModelViewSet的父类:
GenericViewSet 5个视图拓展类(这5个类都直接继承object)
GenericViewSet继承了GenericAPIView和ViewSetMixin(该类直接继承object)
GenericAPIView的继承关系就比较清晰了:
GenericAPIView>>>APIView>>>View>>>object
请求与函数的对应:
get-->list 获取多个
get-->retrieve 获取一个
post-->create
put-->update
delete-->delete
class Book06(ModelViewSet):
queryset = models.Booklib.objects
serializer_class = BookModelSerializer
def list(self, request, *args, **kwargs):
queryset = self.get_queryset()
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data[0:2])#此处使得获取所有数据的请求改为只获取前两个数据,这是通过重写list方法实现的
@action([‘GET‘], False) #通过action装饰器自动生成list_3对应的url,False即不接受参数
def list_3(self, request):
pass
使用步骤:
第一步:导入routers模块
第二步:有两个类,实例化得到对象
第三步:调用对象的注册功能
第四部:自动生成的路由,加入到原路由中
# 1 在urls.py中配置
path(‘books4/‘, views.Book4View.as_view()),
re_path(‘books4/(?P<pk>\d+)‘, views.Book4DetailView.as_view()),
# 2 一旦视图类,继承了ViewSetMixin,路由
path(‘books5/‘, views.Book5View.as_view(actions={‘get‘:‘list‘,‘post‘:‘create‘})), #当路径匹配,又是get请求,会执行Book5View的list方法
re_path(‘books5/(?P<pk>\d+)‘, views.Book5View.as_view(actions={‘get‘:‘retrieve‘,‘put‘:‘update‘,‘delete‘:‘destroy‘})),
# 3 继承自视图类,ModelViewSet的路由写法(自动生成路由)
-urls.py
# 第一步:导入routers模块
from rest_framework import routers
# 第二步:有两个类,实例化得到对象
# routers.DefaultRouter 生成的路由更多
# routers.SimpleRouter
router=routers.DefaultRouter()
# 第三步:调用对象的注册功能
# router.register(‘前缀‘,‘继承自ModelViewSet视图类‘,‘别名‘)
router.register(‘books‘,views.BookViewSet) # 不要加斜杠了
# 第四步
# router.urls # 自动生成的路由,加入到原路由中
# print(router.urls)
# urlpatterns+=router.urls
‘‘‘
-views.py
from rest_framework.viewsets import ModelViewSet
from app01.models import Book
from app01.ser import BookSerializer
class BookViewSet(ModelViewSet):
queryset =Book.objects
serializer_class = BookSerializer
作用:个视图类中自定义的方法也自动生成对应的路由
第一步:from rest_framework import decorators
第二步:第一个参数传入列表,列表中写请求方式[‘GET‘,‘POST‘],第二个参数传布尔值,需要携带参数就写True
# action干什么用?为了给继承自ModelViewSet的视图类中定义的函数也添加路由使用
class BookViewSet(ModelViewSet):
queryset =Book.objects.all()
serializer_class = BookSerializer
# methods第一个参数,传一个列表,列表中放请求方式,
# ^books/get_1/$ [name=‘book-get-1‘] 当向这个地址发送get请求,会执行下面的函数
# detail:布尔类型 如果是True
#^books/(?P<pk>[^/.]+)/get_1/$ [name=‘book-get-1‘]
@action(methods=,detail=True)
def get_1(self,request,pk):
print(pk)
book=self.get_queryset()[:2] # 从0开始截取一条
ser=self.get_serializer(book,many=True)
return Response(ser.data)
# 装饰器,放在被装饰的函数上方,method:请求方式,detail:是否带pk
简单来说就是:认证确定了你是谁
一般使用drf提供的,基于密码的认证,基于session的认证,基于token的认证
写一个类继承BaseAuthentication,重写authenticate方法.认证通过返回user对象,否则抛一个异常
在需要认证的视图类里配置 authentication_classes=[认证类1,认证类2...]
全局使用,在setting.py中配置
REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES":["app01.app_auth.MyAuthentication",]
}
局部使用,写在视图类里,可以有多个认证,从左到右依次执行
authentication_classes=[MyAuthentication]
局部禁用
authentication_classes=[]
REST_FRAMEWORK={ },依次往里添加全局配置,所有与drf有关的全局配置都写在这里
# 写一个认证类 app_auth.py
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from app01.models import UserToken
class MyAuthentication(BaseAuthentication):
def authenticate(self, request):
# 认证逻辑,如果认证通过,返回两个值
#如果认证失败,抛出AuthenticationFailed异常
token=request.GET.get(‘token‘)
if token:
#如果用户携带了token直接以token为过滤条件筛选,有结果token就是对了
#这样通过token这个对象也能找到用户对象了
user_token=UserToken.objects.filter(token=token).first()
# 认证通过
if user_token:
return user_token.user,token
#链表查询返回user对象,反向查询表名小写直接获得表对象
else:
raise AuthenticationFailed(‘认证失败‘)
else:
raise AuthenticationFailed(‘请求地址中需要携带token‘)
实现原理
源码分析:
路径:rest_framework-->views-->APIView-->as_view-->dispatch-->self.initial-->self.perform_authentication 调用了包装后的request类的user属性,其实是个方法
转到rest_framework.request.user -->调用了self._authenticate方法
核心源码:
def _authenticate(self):
for authenticator in self.authenticators: #获取认证类的对象
try:
user_auth_tuple = authenticator.authenticate(self)
#调用认证类对象的authenticate方法
#该方法返回一个元祖或一个异常
except exceptions.APIException:
self._not_authenticated()
raise
if user_auth_tuple is not None:
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple
return
self._not_authenticated()
源码解析
#1,self.authenticators的authenticators从哪来的,它是什么?
self.authenticators说明是调用的对象属性,即Request类的属性-->Request接收了该参数,即由调用者传入的-->调用者是APIView的dispatch,它调用了#self.initialize_request实例化并包装了request对象,
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
parser_context = self.get_parser_context(request)
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
该方法的 self.get_authenticators
def get_authenticators(self):
return [auth() for auth in self.authentication_classes]
当前在APIView类中,self.authentication_classes self会依次去视图类的对象空间->继承了APIView的子类的空间->APIView类的空间->项目settings的空间中查找authentication_classes的值,值就是一个个认证类.所以, #authenticators是一个个认证类产生的对象
#2,重写的authenticate方法需要返回什么结果?
认证类的authenticate方法,认证通过返回一个元祖包含两个值,认证失败抛一个异常
认证通过:
self.user, self.auth = user_auth_tuple 返回的第一个值会赋值个self.user,当前在Request类里,所以这两个值都给了request对象,即以后的request就有了这两个属性
认证失败:
from rest_framework.exceptions import APIException,AuthenticationFailed
抛一个AuthenticationFailed异常会被捕获
权限需要和认证配合使用
权限确定登录用户能不能访问某个接口,没登录的直接pass掉
1)写一个类,继承BasePermission,重写has_permission,如果权限通过,就返回True,不通过就返回False
局部使用
class TestView(APIView):
permission_classes = [app_auth.UserPermission]
全局使用
REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES":["app01.app_auth.MyAuthentication",],
‘DEFAULT_PERMISSION_CLASSES‘: [‘app01.app_auth.UserPermission‘,],
}
局部禁用
class TestView(APIView):
permission_classes = []
from rest_framework.permissions import BasePermission
class UserPermission(BasePermission):
#重写has_permission,如果权限通过,就返回True,不通过就返回False
def has_permission(self, request, view):
# 不是超级用户,不能访问
# 由于认证已经过了,request内就有user对象了,当前登录用户
user=request.user # 当前登录用户
# 如果该字段用了choice,通过get_字段名_display()就能取出choice后面的中文
#print(user.get_user_type_display())
if user.user_type==1:
return True
else:
return False
实现原理
源码分析:
路径:rest_framework-->views-->APIView-->as_view-->dispatch-->self.initial-->self.check_permissions 还在APIView这个视图类里
核心源码:
def check_permissions(self, request):
for permission in self.get_permissions(): #一个个由权限类组成的列表被调用生成对象
if not permission.has_permission(request, self):#调用对象has_permission,获取结果
self.permission_denied(
request, message=getattr(permission, ‘message‘, None)
)
源码解析:
# 1)为什么认证需要到Request类中调用方法,权限不需?
认证去Request调用方法是为了控制APIView的request能否有user这个对象属性以判断用户是否登录, 若用户登录则request对象就有了user对象属性,权限判断在认证之后执行,若用户登录了,request就有 user对象属性了,所以不用去Request类中调用方法.
# 2) self.get_permissions是什么?
和认证的authenticator类似,get_permissions得到一个个由权限类组成的列表
# 3)重写的has_permission需要返回什么结果?
布尔值,有权限就返回True,否则返回False
# 演示一下内置权限的使用:IsAdminUser,控制是否对网站后台有权限的人
# 1 创建超级管理员
# 2 写一个测试视图类
from rest_framework.permissions import IsAdminUser
from rest_framework.authentication import SessionAuthentication
class TestView3(APIView):
authentication_classes=[SessionAuthentication,]
permission_classes = [IsAdminUser]
def get(self,request,*args,**kwargs):
return Response(‘这是22222222测试数据,超级管理员可以看‘)
# 3 超级用户登录到admin,再访问test3就有权限
# 4 正常的话,普通管理员,没有权限看(判断的是is_staff字段)
限制确定访问某个接口的频率
配置了就能使用
全局使用:settings的REST_FRAMEWORK里加入
REST_FRAMEWORK = {
‘DEFAULT_THROTTLE_CLASSES‘: (
‘rest_framework.throttling.AnonRateThrottle‘, 限制匿名用户
‘rest_framework.throttling.UserRateThrottle‘, 限制登录用户(必须是Django内置的登录认证)
),
‘DEFAULT_THROTTLE_RATES‘: {
‘anon‘: ‘3/m‘,
‘user‘: ‘10/m‘,
}
}
局部使用:视图类里添加
throttle_classes = [AnonRateThrottle,UserRateThrottle]
过滤查询结果
安装:pip3 install django-filter
注册,在app中注册
先配置后使用
全局配:
REST_FRAMEWORK = {
‘DEFAULT_FILTER_BACKENDS‘: (‘django_filters.rest_framework.DjangoFilterBackend‘,)
}
局部配:写在视图类里
filter_backends = [DjangoFilterBackend]
使用:
#视图层必须继承了 GenericAPIView或其子类 的视图类才能使用过滤器
#视图层必须配置过滤字段 filter_fields = (‘name‘,)
#一般就是查所有的业务会使用过滤字段
class BookView(ListAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
filter_fields = (‘name‘,)
传参:
http://127.0.0.1:8000/books2/?name=xxx
对查询结果排序
基于filter插件使用
先配置后使用
全局配:
REST_FRAMEWORK = {
‘DEFAULT_FILTER_BACKENDS‘: (‘django_filters.rest_framework.OrderingFilter‘,)
}
局部配:写在视图类里
filter_backends = [OrderingFilter]
使用:
class Book2View(ListAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
filter_backends = [OrderingFilter,DjangoFilterBackend]
#如果全局配置了过滤,这里想局部使用排序,需要在局部里再加入DjangoFilterBackend,因为它们使用同一个 filter_backends,局部配置会的覆盖全局的配置导致局部没有过滤功能
ordering_fields = (‘id‘, ‘price‘)
传参:
http://127.0.0.1:8000/books2/?ordering=-price
http://127.0.0.1:8000/books2/?ordering=price
对查询结果分页,继承了ViewsetMixin的视图类直接配置就用了分页功能
继承了APIView或GenericAPIView的视图类需要手动调用分页器的功能
# 查所有,才需要分页
# 内置三种分页方式依次只能使用一种
from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination
drf分页器默认配置: import rest_framework.settings
DEFAULTS = { ‘DEFAULT_PAGINATION_CLASS‘: None,
‘PAGE_SIZE‘: None,}
PageNumberPagination的使用
1)直接配置使用默认功能:
在项目的settings全局配置:
REST_FRAMEWORK = {‘DEFAULT_PAGINATION_CLASS‘:‘rest_framework.pagination.PageNumberPagination‘,
‘PAGE_SIZE‘: 5, }
在类里局部配置:
pagination_classes=MyPageNumberPagination
默认只能使用一个参数,即PAGE_SIZE 控制信息每页条数
2)自定义分页器类:
写一个类继承分页器类,在类里重写参数,全局配置或局部配置
class MyPageNumberPagination(PageNumberPagination):
#http://127.0.0.1:8000/api/books2/?aaa=1&size=6
page_size=3 #每页条数
page_query_param=‘aaa‘ #查询第几页的key
page_size_query_param=‘size‘ # 每一页显示的条数的key
max_page_size=5 # 每页最大显示条数
class MyLimitOffsetPagination(LimitOffsetPagination):
default_limit = 3 # 每页条数
limit_query_param = ‘limit‘ # 往后拿几条
offset_query_param = ‘offset‘ # 标杆
max_limit = 5 # 每页最大几条
class MyCursorPagination(CursorPagination):
cursor_query_param = ‘cursor‘ # 每一页查询的key
page_size = 2 #每页显示的条数
ordering = ‘-id‘ #排序字段
3)视图类使用分页器
# 使用ListAPIView的视图类分页
class BookView(ListAPIView):
# queryset = models.Book.objects.all().filter(is_delete=False)
queryset = models.Book.objects.all()
serializer_class = BookModelSerializer
#配置分页
pagination_class = MyCursorPagination
# 使用APIView的视图类分页
from utils.throttling import MyThrottle
class BookView(APIView):
# throttle_classes = [MyThrottle,]
def get(self,request,*args,**kwargs):
book_list=models.Book.objects.all()
# 实例化得到一个分页器对象
page_cursor=MyPageNumberPagination()
# 调用分页器的分页功能
book_list=page_cursor.paginate_queryset(book_list,request,view=self)
# 调用获取上下页功能
next_url =page_cursor.get_next_link()
pr_url=page_cursor.get_previous_link()
book_ser=BookModelSerializer(book_list,many=True)
return Response(data=book_ser.data)
插件有coreapi和swagger,这里以coreapi举例
# 1 安装:pip install coreapi
# 2 配置
settings配置:
REST_FRAMEWORK={
‘DEFAULT_SCHENA_CLASSES‘:(‘rest_framework.schemas.coreapi.AutoSchema‘),
}
路由配置:
from rest_framework.documentation import include_docs_urls
urlpatterns = [
path(‘docs/‘, include_docs_urls(title=‘站点页面标题‘))
]
#3 视图类:自动接口文档能生成的是继承自APIView及其子类的视图。
-1 ) 单一方法的视图,可直接使用类视图的文档字符串,如
class BookListView(generics.ListAPIView):
"""
返回所有图书信息.
"""
-2)包含多个方法的视图,在类视图的文档字符串中,分开方法定义,如
class BookListCreateView(generics.ListCreateAPIView):
"""
get:
返回所有图书信息.
post:
新建图书.
"""
-3)对于视图集ViewSet,仍在类视图的文档字符串中封开定义,但是应使用action名称区分,如
class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
"""
list:
返回图书列表数据
retrieve:
返回图书详情数据
latest:
返回最新的图书数据
read:
修改图书的阅读量
"""
所有框架都能用jwt认证,这是一个通用的认证方法
#jwt的组成:
1)jwt分三段式:头.体.签名 (head.payload.signature)
2)#头和体是可逆加密,让服务器可以反解出user对象;签名是不可逆加密,保证整个token的安全性的
3)头体签名三部分,都是采用json格式的字符串,进行加密,可逆加密一般采用base64算法,不可逆加密一般采用hash(md5)算法
4)头中的内容是基本信息:公司信息、项目组信息、token采用的加密方式信息
{
"company": "公司信息",
...
}
5)体中的内容是关键信息:用户主键、用户名、签发时客户端信息(设备号、地址)、过期时间
{
"user_id": 1,
...
}
6)签名中的内容时安全信息:头的加密结果 + 体的加密结果 + 服务器不对外公开的安全码 进行md5加密
{
"head": "头的加密字符串",
"payload": "体的加密字符串",
"secret_key": "安全码"
}
#校验
1)将token按 . 拆分为三段字符串,第一段 头加密字符串 一般不需要做任何处理
2)第二段 体加密字符串,要反解出用户主键,通过主键从User表中就能得到登录用户,过期时间和设备信息都是安全信息,确保token没过期,且时同一设备来的
3)再用 第一段 + 第二段 + 服务器安全码 不可逆md5加密,与第三段 签名字符串 进行碰撞校验,通过后才能代表第二段校验得到的user对象就是合法的登录用户
? 别人已经写好了认证过程,配合user表调用即可使用,或者自定义jwt的使用
#token的签发:
1)用账号密码访问登录接口,
2) 登录接口逻辑中调用方法签发token,得到token,返回给客户端
#token的校验与反解
3)校验token,算法应该写在认证类中(在认证类中调用),
4) 请求带了token,就会反解出user对象,在视图类中用request.user就能访问登录的用户
安装 pip install djangorestframework-jwt
#在路由层导入obtain_jwt_token
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
path(‘login/‘, obtain_jwt_token),
]
#视图类不需要自己写,使用obtain_jwt_token就能调用jwt组件已经写好的校验视图类,自动去user表取值校验并返回token
#修改自动签发token的返回格式:在项目而settings加入
def my_jwt_response_payload_handler(token, user=None, request=None):
return {
‘status‘:100,
‘msg‘:‘认证成功‘
‘token‘: token
}
JWT_AUTH{
‘JWT_RESPONSE_PAYLOAD_HANDLER‘:
(‘rest_framework_jwt.utils.my_jwt_response_payload_handler‘,)
‘JWT_EXPIRATION_DELTA‘:datetime.timedelta(days=7), #配置token过期时间
}
限定登录用户才能访问的接口需要token和promission配合使用
token判断用户是否登录,promission判断用户是否有权限访问
JSONWebTokenAuthentication,是已经写好的认证类
在视图类的authentication_classes里加入JSONWebTokenAuthentication,该视图类就具有认证token的功能.
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated
class OrderAPIView(APIView):
authentication_classes = [JSONWebTokenAuthentication,] # 登录才能
permission_classes = [IsAuthenticated,] # 权限控制
def get(self,request,*args,**kwargs):
return Response(‘这是订单信息‘)
# 使用用户名,手机号,邮箱,都可以登录#
# 前端需要传的数据格式
{
"username":"lqz/1332323223/33@qq.com",
"password":"lqz12345"
}
# 视图
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin, ViewSet
from app02 import ser
class Login2View(ViewSet):
def login(self, request, *args, **kwargs):
# 1 需要 有个序列化的类
login_ser = ser.LoginModelSerializer(data=request.data,context={‘request‘:request})
# 2 生成序列化类对象
# 3 调用序列号对象的is_validad
login_ser.is_valid(raise_exception=True)
token=login_ser.context.get(‘token‘)
username=login_ser.context.get(‘username‘)
# 4 return
return Response({‘status‘:100,‘msg‘:‘登录成功‘,‘token‘:token,‘username‘:username})
# 序列化类
from rest_framework import serializers
from api import models
import re
from rest_framework.exceptions import ValidationError
from rest_framework_jwt.utils import jwt_encode_handler,jwt_payload_handler
class LoginModelSerializer(serializers.ModelSerializer):
username=serializers.CharField()
# 重新覆盖username字段,数据中它是unique,post,认为你保存数据,自己会校验
class Meta:
model=models.User
fields=[‘username‘,‘password‘]
def validate(self, attrs):
# 重写了父类的validate方法,在该方法里写额外的校验逻辑,校验时执行该方法
username=attrs.get(‘username‘) # 用户名有三种方式
password=attrs.get(‘password‘)
# 通过判断,username数据不同,查询字段不一样
# 正则匹配,如果是手机号
if re.match(‘^1[3-9][0-9]{9}$‘,username):
user=models.User.objects.filter(mobile=username).first()
elif re.match(‘^.+@.+$‘,username):# 邮箱
user=models.User.objects.filter(email=username).first()
else:
user=models.User.objects.filter(username=username).first()
if user: # 存在用户
# 校验密码,因为是密文,要用user表的check_password方法
if user.check_password(password):
# 手动签发token
payload = jwt_payload_handler(user) # 把user传入,得到payload
token = jwt_encode_handler(payload) # 把payload传入,得到token
self.context[‘token‘]=token
self.context[‘username‘]=user.username
return attrs
else:
raise ValidationError(‘密码错误‘)
else:
raise ValidationError(‘用户不存在‘)
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.utils import jwt_decode_handler
from jwt import exceptions
class MyJwtAuthentication(BaseJSONWebTokenAuthentication):
def authenticate(self, request):#只要是自定义的就要重写这个方法
jwt_value=request.META.get(‘HTTP_AUTHORIZATION‘) #获取token
if jwt_value:
try:
payload=jwt_decode_handler(jwt_value) #校验token并取出payload
except ExpiredSignature:
raise AuthenticationFailed(‘签名过期‘)
except InvalidTokenError:
raise AuthenticationFailed(‘用户非法‘)
except Exception as e:
raise AuthenticationFailed(str(e)) # 其他所有异常都会走到这
user=self.authenticate_credentials(payload) #获取user对象
return user,jwt_value
raise AuthenticationFailed(‘您没有携带认证信息‘) # 没有值,直接抛异常
#局部配置,全局配置
开发调试缓存
CACHES = {
‘default‘: {
‘BACKEND‘: ‘django.core.cache.backends.dummy.DummyCache‘, # 缓存后台使用的引擎
‘TIMEOUT‘: 300, # 缓存超时时间(默认300秒,None表示永不过期,0表示立即过期)
‘OPTIONS‘:{
‘MAX_ENTRIES‘: 300, # 最大缓存记录的数量(默认300)
‘CULL_FREQUENCY‘: 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
},
}
}
内存缓存
CACHES = {
‘default‘: {
‘BACKEND‘: ‘django.core.cache.backends.locmem.LocMemCache‘, # 指定缓存使用的引擎
‘LOCATION‘: ‘unique-snowflake‘, # 写在内存中的变量的唯一值
‘TIMEOUT‘:300, # 缓存超时时间(默认为300秒,None表示永不过期)
‘OPTIONS‘:{
‘MAX_ENTRIES‘: 300, # 最大缓存记录的数量(默认300)
‘CULL_FREQUENCY‘: 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
}
}
}
文件缓存
CACHES = {
‘default‘: {
‘BACKEND‘: ‘django.core.cache.backends.filebased.FileBasedCache‘, #指定缓存使用的引擎
‘LOCATION‘: ‘/var/tmp/django_cache‘, #指定缓存的路径
‘TIMEOUT‘:300, #缓存超时时间(默认为300秒,None表示永不过期)
‘OPTIONS‘:{
‘MAX_ENTRIES‘: 300, # 最大缓存记录的数量(默认300)
‘CULL_FREQUENCY‘: 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
}
}
}
数据库缓存
CACHES = {
‘default‘: {
‘BACKEND‘: ‘django.core.cache.backends.db.DatabaseCache‘, # 指定缓存使用的引擎
‘LOCATION‘: ‘cache_table‘, # 数据库表
‘OPTIONS‘:{
‘MAX_ENTRIES‘: 300, # 最大缓存记录的数量(默认300)
‘CULL_FREQUENCY‘: 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
}
}
}
Memcache缓存(使用python-memcached模块)
Memcached是Django原生支持的缓存系统.要使用Memcached,需要下载Memcached的支持库python-memcached或pylibmc.
CACHES = {
‘default‘: {
‘BACKEND‘: ‘django.core.cache.backends.db.DatabaseCache‘, # 指定缓存使用的引擎
‘LOCATION‘: ‘cache_table‘, # 数据库表
‘OPTIONS‘:{
‘MAX_ENTRIES‘: 300, # 最大缓存记录的数量(默认300)
‘CULL_FREQUENCY‘: 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
}
}
}
Memcache缓存(使用pylibmc模块)
settings.py文件配置
CACHES = {
‘default‘: {
‘BACKEND‘: ‘django.core.cache.backends.memcached.PyLibMCCache‘, # 指定缓存使用的引擎
‘LOCATION‘:‘192.168.10.100:11211‘, # 指定本机的11211端口为Memcache缓存服务器
‘OPTIONS‘:{
‘MAX_ENTRIES‘: 300, # 最大缓存记录的数量(默认300)
‘CULL_FREQUENCY‘: 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
},
}
}
视图:
from django.views.decorators.cache import cache_page
import time
from .models import *
@cache_page(15) #超时时间为15秒
def index(request):
t=time.time() #获取当前时间
bookList=Book.objects.all()
return render(request,"index.html",locals())
模板(index.html):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>当前时间:-----{{ t }}</h3>
<ul>
{% for book in bookList %}
<li>{{ book.name }}--------->{{ book.price }}$</li>
{% endfor %}
</ul>
</body>
</html>
上面的例子是基于内存的缓存配置,基于文件的缓存该怎么配置呢??
更改settings.py的配置
CACHES = {
‘default‘: {
‘BACKEND‘: ‘django.core.cache.backends.filebased.FileBasedCache‘, # 指定缓存使用的引擎
‘LOCATION‘: ‘E:\django_cache‘, # 指定缓存的路径
‘TIMEOUT‘: 300, # 缓存超时时间(默认为300秒,None表示永不过期)
‘OPTIONS‘: {
‘MAX_ENTRIES‘: 300, # 最大缓存记录的数量(默认300)
‘CULL_FREQUENCY‘: 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
}
}
}
然后再次刷新浏览器,可以看到在刚才配置的目录下生成的缓存文件
通过实验可以知道,Django会以自己的形式把缓存文件保存在配置文件中指定的目录中.
既然是全站缓存,当然要使用Django中的中间件.
用户的请求通过中间件,经过一系列的认证等操作,如果请求的内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户
当返回给用户之前,判断缓存中是否已经存在,如果不存在,则UpdateCacheMiddleware会将缓存保存至Django的缓存之中,以实现全站缓存
缓存整个站点,是最简单的缓存方法
在 MIDDLEWARE_CLASSES 中加入 “update” 和 “fetch” 中间件
MIDDLEWARE_CLASSES = (
‘django.middleware.cache.UpdateCacheMiddleware’, #第一
‘django.middleware.common.CommonMiddleware‘,
‘django.middleware.cache.FetchFromCacheMiddleware’, #最后
)
“update” 必须配置在第一个
“fetch” 必须配置在最后一个
修改settings.py配置文件
视图函数:
from django.views.decorators.cache import cache_page
import time
from .models import *
def index(request):
t=time.time() #获取当前时间
bookList=Book.objects.all()
return render(request,"index.html",locals())
def foo(request):
t=time.time() #获取当前时间
return HttpResponse("HELLO:"+str(t))
模板(index.html):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3 style="color: green">当前时间:-----{{ t }}</h3>
<ul>
{% for book in bookList %}
<li>{{ book.name }}--------->{{ book.price }}$</li>
{% endfor %}
</ul>
</body>
</html>
其余代码不变,刷新浏览器是10秒,页面上的时间变化一次,这样就实现了全站缓存.
例子,刷新页面时,整个网页有一部分实现缓存
views视图函数
from django.views.decorators.cache import cache_page
import time
from .models import *
def index(request):
t=time.time() #获取当前时间
bookList=Book.objects.all()
return render(request,"index.html",locals())
模板(index.html):
{% load cache %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3 style="color: green">不缓存:-----{{ t }}</h3>
{% cache 2 ‘name‘ %}
<h3>缓存:-----:{{ t }}</h3>
{% endcache %}
</body>
</html>
from rest_framework.serializers import ModelSerializer # drf的序列化器
from rest_framework.request import Request # drf的请求
from rest_framework.response import Response # drf的响应
from rest_framework.exceptions import APIException #所有API相关的异常
from rest_framework.views import APIView # drf的视图类
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import CreateModelMixin
from rest_framework.viewsets import ViewSetMixin
from rest_framework.authentication import BaseAuthentication #drf的认证类
from rest_framework.permissions import BasePermission # drf的权限类
from rest_framework.throttling import BaseThrottle # drf的限流类
from rest_framework.permissions import IsAdminUser # Django内置的权限认证
from rest_framework.authentication import SessionAuthentication # Django内置的登录认证
from jwt import exceptions # jwt相关的异常
import rest_framework_jwt.settings #jwt的默认配置
from rest_framework_jwt.views import ObtainJSONWebToken #jwt提供的视图类及其他
from rest_framework_jwt.authentication import JSONWebTokenAuthentication #jwt的认证类
from rest_framework_jwt.authentication import jwt_decode_handler #和utils里的是同一个
from rest_framework_jwt.utils import jwt_response_payload_handler #jwt自动签发token返回器
from rest_framework_jwt.utils import jwt_decode_handler,jwt_payload_handler #jwt解码器
import untitled4.settings # 该项目的配置
import rest_framework_jwt.settings #jwt的默认配置
import rest_framework.settings #drf所有的默认配置
import rest_framework.status #drf所有的状态码
from rest_framework.views import exception_handler #drf默认异常处理器
from django_filters.rest_framework import DjangoFilterBackend # 过滤器筛选器
from rest_framework.schemas.coreapi import AutoSchema # 自动生成接口
from rest_framework.pagination import PageNumberPagination # 分页器
原文:https://www.cnblogs.com/Franciszw/p/13341191.html