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