Django DRF 详解

Django DRF

目录

1,RESTful API规范

REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征性状态转移)。 它首次出现在2000年Roy Fielding的博士论文中。

RESTful是一种定义Web API接口的设计风格,尤其适用于前后端分离的应用模式中。

这种风格的理念认为后端开发任务就是提供数据的,对外提供的是数据资源的访问接口,所以在定义接口时,客户端访问的URL路径就表示这种要操作的数据资源。

事实上,我们可以使用任何一个框架都可以实现符合restful规范的API接口。

1.1 数据的安全保障

  • url链接一般都采用https协议进行传输

    注:采用https协议,可以提高数据交互过程中的安全性

1.2 接口特征表现

1.3 多数据版本共存

1.4 数据即是资源,均使用名词(可复数)

1.5 资源操作由请求方式决定(method)

1.6 过滤,通过在url上传参的形式传递搜索条件

1.7 响应状态码

1.7.1 正常响应

  • 响应状态码2xx
    • 200:常规请求
    • 201:创建成功

1.7.2 重定向响应

  • 响应状态码3xx
    • 301:永久重定向
    • 302:暂时重定向

1.7.3 客户端异常

  • 响应状态码4xx
    • 403:请求无权限
    • 404:请求路径不存在
    • 405:请求方法不存在

1.7.4 服务器异常

  • 响应状态码5xx
    • 500:服务器异常

1.8 错误处理,应返回错误信息,error当做key

{
    error: "无权限操作"
}

1.9 返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范

GET /collection:返回资源对象的列表(数组)
GET /collection/resource:返回单个资源对象
POST /collection:返回新生成的资源对象
PUT /collection/resource:返回完整的资源对象
PATCH /collection/resource:返回完整的资源对象
DELETE /collection/resource:返回一个空文档

1.10 需要url请求的资源需要访问资源的请求链接

# 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"
        }
      	...
		]
}

2 序列化器

作用:

1. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串
2. 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型
3. 反序列化,完成数据校验功能

2.1 继承Serializer类的序列化器

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是独立于数据库之外的存在。

2.2 继承与ModelSerializer的序列化器

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},
            }

2.3 序列化器的使用

序列化器的使用分两个阶段:

  1. 在客户端请求时,使用序列化器可以完成对数据的反序列化。
  2. 在服务器响应时,使用序列化器可以完成对数据的序列化。

2.3.1基本使用

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参数

  1. 同时传入 instance,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)

2.3.2 高级用法 source

3,请求,响应,异常

3.1 DRF Request

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对象的数据是自动根据前端发送数据的格式进行解析之后的结果。

无论前端发送的哪种格式的数据,我们都可以以统一的方式读取数据。

Request常用属性

1).data

request.data 返回解析之后的请求体数据。类似于Django中标准的request.POSTrequest.FILES属性,但提供如下特性:

  • 包含了解析之后的文件和非文件数据
  • 包含了对POST、PUT、PATCH请求方式解析后的数据
  • 利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据

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请求版本号

3.2 DRF Response

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渲染器
    )
}

3.2.1 使用

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会根据前端所需类型数据来设置该参数。

3.2.1 Response的常用属性

1).data

传给response对象的序列化后,但尚未render处理的数据

2).status_code

状态码的数字

3).content

经过render处理后的响应数据

3.2.3 状态码

为了方便设置状态码,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

3.2.4 自定义Response

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是服务器处理状态码,浏览器会根据该状态码做出反应,一般不需要手动填写

3.3自定义异常处理

作用:

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)

4,视图类集合,路由

4.1 基于APIView写接口:

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': '删除成功'})

下面的类分别解简化了上述步骤大量减少代码的书写:

4.2 基于GenericAPIView写的接口:

简化步骤,在类里直接申明了操作哪张表,调用哪个序列化器:

queryset = models.Booklib.objects
serializer_class = BookModelSerializer

请求的函数调用申明的列表和序列化器会自动传入对象

book=self.get_queryset().filter(pk=num).first()
ser_obj=self.get_serializer(book)

下面分别对应5种方法的注意事项:

  • queryset只有一个对象时,需要用.first()取到
  • 反序列化数据化后要判断数据是否合法,调用序列化器的save方法
  • 多条数据序列化用many=True
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='数据不合法')

4.3 基于GenericAPIView和5个视图扩展类写的接口

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)

2.4 基于GenericAPIView的9个视图拓展类编写接口:

分别是:

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

4.5继承ViewSetMixin的视图类

作用:只需要编写一个视图类来对应两个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'})),

4.6 基于ModelViewSet类编写接口

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

4.7 自动生成路由

使用步骤:

第一步:导入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

2.7.1 action的使用

作用:个视图类中自定义的方法也自动生成对应的路由

第一步: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

5,认证,权限,频率

5.1 认证

简单来说就是:认证确定了你是谁

一般使用drf提供的,基于密码的认证,基于session的认证,基于token的认证

5.1.1自定义认证组件的使用:

  1. 写一个类继承BaseAuthentication,重写authenticate方法.认证通过返回user对象,否则抛一个异常

  2. 在需要认证的视图类里配置 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异常会被捕获

5.1.2配合Auth模块认证组件的使用:


5.2 权限

权限需要和认证配合使用

权限确定登录用户能不能访问某个接口,没登录的直接pass掉

5.2.1自定义权限组件的使用

1)写一个类,继承BasePermission,重写has_permission,如果权限通过,就返回True,不通过就返回False

  1. 使用, 在需要认证的视图类里配置 permission_classes =[权限类1,权限类2...]

局部使用

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


5.2.2配合Auth模块权限组件的使用

# 演示一下内置权限的使用: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字段)

5.3 频率

限制确定访问某个接口的频率

5.3.1 自定义频率组件的使用


5.3.2 配合Auth表使用频率组件

配置了就能使用

全局使用: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]
    

6,过滤,排序,分页,自动生成接口文档

6.1 过滤器

过滤查询结果

安装: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 

6.2 排序器

对查询结果排序

基于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
    

6.3 分页器

对查询结果分页,继承了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)

    

6.3 自动生成接口文档

插件有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:
        修改图书的阅读量
        """

7. JWT

7.1 jwt基本原理

所有框架都能用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对象就是合法的登录用户

7.2 Django使用jwt认证

​ 别人已经写好了认证过程,配合user表调用即可使用,或者自定义jwt的使用

7.2.1 drf项目的jwt认证开发流程

#token的签发:
1)用账号密码访问登录接口,
2) 登录接口逻辑中调用方法签发token,得到token,返回给客户端

#token的校验与反解
3)校验token,算法应该写在认证类中(在认证类中调用),
4) 请求带了token,就会反解出user对象,在视图类中用request.user就能访问登录的用户

7.2.2 Django jwt认证的使用

安装 pip install djangorestframework-jwt

7.3 配合user表使用jwt

7.3.1自动签发token

#在路由层导入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过期时间


}

7.3.2 自动认证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('这是订单信息')

7.4 自定义jwt认证系列:

7.4.1手动签发token+多方式登录

# 使用用户名,手机号,邮箱,都可以登录#
# 前端需要传的数据格式
{
"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('用户不存在')

7.4.2自定义jwt认证类

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('您没有携带认证信息') # 没有值,直接抛异常

#局部配置,全局配置        

8,缓存

8.1 Django中的6种缓存方式配置

  • 开发调试缓存

    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)
       },  
      }
     }
    

8.2 Django中的缓存应用

8.2.1 视图函数使用缓存

视图:

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会以自己的形式把缓存文件保存在配置文件中指定的目录中.

8.2.1 全站使用缓存

既然是全站缓存,当然要使用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秒,页面上的时间变化一次,这样就实现了全站缓存.

8.2.2局部视图缓存

例子,刷新页面时,整个网页有一部分实现缓存

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> 

9,drf常用导入模块路径

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