zoukankan      html  css  js  c++  java
  • Python-Django之DRF

    DRF

    DRF(Django REST framework)框架是建立在Django框架基础之上,本质上它就是Django的一个App,通过DRF能够快速设计符合RESTful规范的接口,并且它还提供了一些功能。

    RESTful规范

    RESTful规范锁一套接口的协议,用于程序与程序见数据交换的一套默认规则

    RESTful规范规定了以下几点:

    • https代替http,保证数据传输时安全。
    • 在url中一般要体现api标识
    • 在接口中要体现版本
    • restful也称为面向资源编程,一般资源都使用名词
    • 筛选条件要添加在url中
    • 根据method不同做不同操作
    • 返回状态码
    • 返回值
      • 增加数据时返回新增的数据
      • 单条查询返回字典
      • 多条查询返回列表
      • 更新返回更新后的完整数据
      • 删除返回空
    • 操作异常时,要返回错误信息
    • 返回下一个请求的接口
    示例:
    http://www.takanashi.com/api/v1/user/?page=1
    

    安装

    pip3 install djangorestframework
    

    使用

    drf本质是一个app,所以如果想要在Django项目中使用它就需要在settings配置文件中进行注册

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'rest_framework'
    ]
    
    

    使用drf时推荐在视图中使用CBV处理请求

    # urls.py
    from app1 import views
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^api/book/$', views.Book.as_view()),
        url(r'^api/book/(?P<pk>d+)/$', views.Book.as_view()),
    ]
    
    

    视图中根据请求不同做不同的处理,返回json格式的数据

    # views.py
    from rest_framework.views import APIView
    from rest_framework.response import Response
    # Create your views here.
    from app1 import models
    from django.forms.models import model_to_dict
    class Book(APIView):
        def get(self,request,*args,**kwargs):
            # 查询
            pk = kwargs.get("pk")
            if pk:
                book = models.Book.objects.filter(pk=pk).first()
                return Response(model_to_dict(book))
            books = models.Book.objects.all().values()
            return Response(books)
        def post(self,request,*args,**kwargs):
            # 增加
            book = models.Book.objects.create(**request.data)
            return Response(model_to_dict(book))# model_to_dict将一个对象转换为字典
        def put(self,request,*args,**kwargs):
            # 修改
            pk = kwargs.get("pk")
            models.Book.objects.filter(id=pk).update(**request.data)
            return Response("ok")
        def delete(self,request,*args,**kwargs):
            # 删除
            pk = kwargs.get("pk")
            models.Book.objects.get(pk=pk).delete()
            return Response("ok")
    

    序列化

    drf中的serializers为我们提供了数据校验、序列化的功能

    基于APIView实现

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from app1 import models
    # 导入serializers
    from rest_framework import serializers
    class BookSerializers(serializers.ModelSerializer):
        # 自定义字段:
        # 方式一:source指定显示内容,required指定校验时忽略
        title_1 = serializers.CharField(source="title",required=False)
        # 方式二:这种写法需要写一个以get_开头的钩子函数,这个函数返回上面就显示什么
        state_1 = serializers.ModelSerializer()
        # 如果想要返回orm中使用了choices的字段只需要这样写,不需要加括号,内部会检查是否可执行,如果可执行会自动加括号
        state_2 = serializers.CharField(source="get_status_display",required=False)
        class Meta:
            # 指定表
            model = models.Book
            # 指定字段,全部字段、排除指定字段、选择指定字段
            # fields = "__all__"
            # exclude = ["id"]
            fields = ["id","title","title_1","title_2","state_1","state_2"]
            
        # 钩子函数,obj就是当前对象
        def get_title_2(self,obj):
            return obj.title
    
        def get_state_1(self, obj):
            return obj.get_status_display()
    
    class Book(APIView):
        
        def get(self,request,*args,**kwargs):
            pk = kwargs.get("pk")
            if pk:
                book = models.Book.objects.filter(pk=pk).first()
                # 实例化序列化对象,instance指定数据,many默认等于False,表示查询结果为一条数据
                ser = BookSerializers(instance=book,many=False)
                return Response(ser.data)
            books = models.Book.objects.all().values()
            # many=True表示查询结果有多条数据
            ser = BookSerializers(instance=books,many=True)
            return Response(ser.data)
        
        def post(self,request,*args,**kwargs):
            # data指定保存的数据
            ser = BookSerializers(data=request.data)
            # 校验数据
            if ser.is_valid():
                # 保存
                ser.save()
                # 返回保存的数据
                return Response(ser.data)
            # 返回错误信息
            return Response(ser.errors)
        
        def put(self,request,*args,**kwargs):
            # 全部修改
            pk = kwargs.get('pk')
            book = models.Book.objects.filter(id=pk).first()
            # instance指定修改哪条数据,data指定钥修改为的数据
            ser = BookSerializers(instance=book, data=request.data)
            if ser.is_valid():
                ser.save()
                return Response(ser.data)
            return Response(ser.errors)
    
        def patch(self, request, *args, **kwargs):
            # 局部修改
            pk = kwargs.get('pk')
            book = models.Book.objects.filter(id=pk).first()
            # partial=True指定此次修改为局部修改
            ser = BookSerializers(instance=book,data=request.data,partial=True)
            if ser.is_valid():
                ser.save()
                return Response(ser.data)
            return Response(ser.errors)
    

    基于ListAPIView实现

    from app1 import models
    from rest_framework import serializers
    from rest_framework.generics import GenericAPIView,ListAPIView
    
    class BookSerializers(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            fields = "__all__"
            
    class Book(ListAPIView):
        queryset = models.Book.objects.all()
        # 序列化
        serializer_class = BookSerializers
    

    筛选

    基于APIView筛选

    from app1 import models
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework import serializers
    from rest_framework.generics import GenericAPIView,ListAPIView
    class BookSerializers(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            fields = "__all__"
    
    class Book(APIView):
        def get(self,request,*args,**kwargs):
            pk = kwargs.get("pk")
            if pk:
                book = models.Book.objects.filter(pk=pk).first()
                ser = BookSerializers(instance=book,many=False)
                return Response(ser.data)    
            condition = {}
            # 获取条件
            title = request.query_params.get('title')
            if title:
                condition['title'] = title
            books = models.Book.objects.filter(**condition).order_by('pk')
            ser = BookSerializers(instance=books,many=True)
            return Response(ser.data)
    

    基于ListAPIView筛选

    from app1 import models
    from rest_framework import serializers
    from rest_framework.generics import GenericAPIView,ListAPIView
    # 导入BaseFilterBackend
    from rest_framework.filters import BaseFilterBackend
    
    class BookSerializers(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            fields = "__all__"
    # 自定义分页类
    class MyFilterBackend(BaseFilterBackend):
        # 必须实现filter_queryset方法
        def filter_queryset(self, request, queryset, view):
            val = request.query_params.get('title')
            return queryset.filter(title=val)
        
    class Book(ListAPIView):
        queryset = models.Book.objects.all()
        # 序列化
        serializer_class = BookSerializers
        # 分页
        filter_backends = [MyFilterBackend,]
    

    分页

    基于APIView分页

    方式一:

    通过PageNumberPagination类进行分页,访问URL使用以下方式:http://api.example.org/accounts/?page=4

    在settings配置中写入以下配置

    REST_FRAMEWORK  = {
        # 默认显示数
        "PAGE_SIZE":2,
    }
    1234
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from app1 import models
    from rest_framework import serializers
    # 导入PageNumberPagination
    from rest_framework.pagination import PageNumberPagination
    class BookSerializers(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            fields = "__all__"
    
    class Book(APIView):
        def get(self,request,*args,**kwargs):
            queryset = models.Book.objects.all()
            # 实例化分页类
            page_obj = PageNumberPagination()
            # 调用paginate_queryset方法对数据进行分页
            result = page_obj.paginate_queryset(queryset,request,self)
            print(result) # 结果为列表
            # 对分页结果进行序列化
            ser = BookSerializers(instance=result,many=True)
            # 只返回数据
            return Response(ser.data)
            # 返回包含数据、总数据个数、上一页url、下一页url,也可以通过重写get_paginated_response方法自定义显示内容
            return page_obj.get_paginated_response(ser.data)
    

    方式二:

    通过LimitOffsetPagination类进行分页,访问URL使用以下方式:http://127.0.0.1:8000/api/book/?limit=2&offset=2,limit指定显示几条数据,offset指定从第几条数据开始

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from app1 import models
    from rest_framework import serializers
    # 导入LimitOffsetPagination
    from rest_framework.pagination import LimitOffsetPagination
    class BookSerializers(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            fields = "__all__"
    
    class Book(APIView):
        def get(self,request,*args,**kwargs):
            queryset = models.Book.objects.all()
            page_obj = LimitOffsetPagination()
            result = page_obj.paginate_queryset(queryset,request,self)
            ser = BookSerializers(instance=result,many=True)
            return Response(ser.data)
    

    基于ListAPIView分页

    在settings配置中写入以下配置

    REST_FRAMEWORK  = {
        # 默认显示数
        "PAGE_SIZE":2,
        # 指定分页类
        "DEFAULT_PAGINATION_CLASS":"rest_framework.pagination.PageNumberPagination"
    }
    123456
    from app1 import models
    from rest_framework import serializers
    from rest_framework.generics import GenericAPIView,ListAPIView
    class BookSerializers(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            fields = "__all__"
    
    class Book(ListAPIView):
        queryset = models.Book.objects.all()
        # 序列化
        serializer_class = BookSerializers
    

    版本

    局部使用

    urls.py

    urlpatterns = [
        url(r'^api/(?P<version>w+)/book/$', views.Book.as_view()),
    ]
    

    views.py

    from rest_framework.views import APIView
    from rest_framework.response import Response
    # 导入
    from rest_framework.versioning import URLPathVersioning
    class Book(APIView):
        versioning_class = URLPathVersioning
        def get(self,reqeust,*args,**kwargs):
            print(reqeust.version)
            return Response("ok")
    

    全局配置

    想要在所有视图中都应用版本,可以在settings中进行配置

    REST_FRAMEWORK  = {
    	# 配置版本
        "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
        # 配置允许版本
        "ALLOWED_VERSIONS": ['v1', 'v2'],
        # 配置url传参时的名称,默认为version
        'VERSION_PARAM': 'version'
    }
    

    认证

    局部使用

    from app1 import models
    from rest_framework.views import APIView
    from rest_framework.response import Response
    # 导入BaseAuthentication
    from rest_framework.authentication import BaseAuthentication
    class TokenAuthentication(BaseAuthentication):
    	# 必须实现authenticate
        def authenticate(self, request):
        	# 获取url中的token
            token = request.query_params.get("token")
            book_object = models.Book.objects.filter(token=token).first()
            if book_object:
                return (book_object,token) # 认证成功,不再进行认证
                return None # 认证成功,执行下一个认证类
                
            return Exception() # 认证失败,返回给用户
    
    class Book(APIView):
        authentication_classes = [TokenAuthentication]
        def get(self,reqeust,*args,**kwargs):
            print(reqeust.user) # book_object对象
            print(reqeust.auth) # token
            return Response("ok")
    

    全局配置

    settings中配置后所有视图都进行认证

    REST_FRAMEWORK  = {
    	# 自定义Authentication的路径
        'DEFAULT_AUTHENTICATION_CLASSES' : ["authon.auth.TokenAuthentication",]
    }
    

    权限

    根据认证组件的结果判断是否有权限访问

    局部使用

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.permissions import BasePermission
    from rest_framework import exceptions
    
    class MyPermission(BasePermission):
        def has_permission(self, request, view):
            if request.user:
            	# 返回True表示通过,进行下一个权限认证类,
                return True
            # 返回False表示认证未通过由源码内抛出异常
            return False
            # 也可以直接手动抛出异常
            # return exceptions.PermissionDenied({"code":"1001","error":"错误信息"})
    
    	# RetrieveAPIView触发
        def has_object_permission(self, request, view, obj):
            
            return True
    
    class Book(APIView):
        permission_classes = [MyPermission,]
        def get(self,reqeust,*args,**kwargs):
            return Response("ok")
    

    全局配置

    REST_FRAMEWORK  = {
    	# 自定义Permission的路径
        'DEFAULT_PERMISSION_CLASSES':["authon.auth.MyPermission"]
    }
    

    使用全局配置后如果对某一视图不想应用该功能可以通过指定空列表的方式使其不生效

    class Book(APIView):
        permission_classes = []
        def get(self,reqeust,*args,**kwargs):
            return Response("ok")
    

    频率限制

    def的频率限制本质是在内部维护了一个字典,字典以throttle_anon_用户ip做key值是一个列表,当用户请求到来时获取当前时间,然后用当前时间减去60得到一个时间戳,将列表中小于这个时间戳的记录删除,然后获取列表中元素的个数,判断是否可以进行访问,如果可以进行访问则将当前时间插入到列表第一位。

    局部使用

    在settings中配置

    REST_FRAMEWORK  = {
    	# 配置访问次数限制	3/m 每分钟3次
        'DEFAULT_THROTTLE_RATES':{"anon":'3/m'},
    }
    1234
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework.throttling import AnonRateThrottle,BaseThrottle
    
    class Book(APIView):
        throttle_classes = [AnonRateThrottle, ]
        def get(self,reqeust,*args,**kwargs):
            return Response("ok")
    

    全局使用

    在settings中配置

    REST_FRAMEWORK  = {
    	# 配置访问次数限制	3/m 每分钟3次
        'DEFAULT_THROTTLE_RATES':{"anon":'3/m'},
        # 配置AnonRateThrottle路径
        'DEFAULT_THROTTLE_CLASSES':["rest_framework.throttling.AnonRateThrottle"]
    }
    

    DRF提供的APIView

    from rest_framework.generics import ListAPIView,CreateAPIView,RetrieveAPIView,UpdateAPIView,DestroyAPIView
    from rest_framework import serializers
    from app1 import models
    
    class BookSerializers(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            fields = "__all__"
            
    class Book(ListAPIView,CreateAPIView):
        # 列表数据查询、增加数据
        queryset = models.Book.objects.all()
        serializer_class = BookSerializers
    
    class BookDetailView(RetrieveAPIView,UpdateAPIView,DestroyAPIView):
        # 单条数据查询、修改数据、删除数据
        queryset = models.Book.objects.all()
        serializer_class = BookSerializers
    

    DRF源码分析

    在这里插入图片描述
    预览链接

    1.启动项目:

    views.Book.as_view()该条代码会执行APIView中的as_view方法,这个方法内部首先会执行父类(View)的as_view方法,父类方法返回一个view函数,当其请求到来时执行这个函数。

    2.请求到来:

    请求到来首先执行之前得到的view函数,在函数内部调用了APIView函数的dispath函数,在这个函数内部执行initialze_request函数对request进行封装:

    • 将原本的request封装为_reqeust
    • 读取视图函数中的认证列表authentication_classes并将认证列表封装。

    执行完这些后接着返回dispath函数执行的initial,在这个函数中首先会做版本的处理:

    • 调用determine_version读取视图中的版本类versioning_class

    • 执行

      versioning_class
      

      指定的类的

      determine_version
      

      函数进行版本处理:

      • 获取url中的版本号
      • 调用is_allowed_version函数校验版本号合法性

    版本处理结束后会进行认证:

    • 调用 perform_authentication执行reqeust.user

    • Reuest类的

      user
      

      会调用

      _authenticator
      

      函数进行认证

      • 循环认证列表执行每个认证类的authenticator函数
      • authenticator函数中做认证处理
      • 得到认证结果将authenticator函数返回的第一个值封装到self.user中,将第二个值封装到self.auth

    认证处理结束后进行权限处理:

    • 调用 check_throttles函数
    • 函数内部通过get_permissions函数读取视图中的权限列表permission_classes
    • 循环权限列表执行列表中每个类的has_permissions函数进行权限认证

    权限处理结束后进行频率限制:

    • 调用 check_throttles函数

    • 函数内部通过get_throttles函数读取视图中的频率限制列表throttles_classes

    • 循环频率限制列表执行列表中每个类的allow_request函数

    • allow_request
      

      进行频率限制:

      • 获得历史请求记录列表
      • 将列表中失效的记录删除
      • 通过列表长度判断是否可以访问
      • 可以访问:将当前时间加到记录列表的第一位‘’

    3.执行视图函数:

    当上面所有步骤都结束后在dispath函数中会通过反射执行视图中对应请求的函数,“以ListAPIView的get请求举例”:

    • 执行ListAPIView中的get函数,函数内调用ListModeMixin的list函数

    • 在list函数中获得视图中的queryset

    • 执行

      filter_quertset
      

      对queryset进行筛选:

      • 读取视图中的filter_backends,得到筛选类列表
      • 循环筛选类列表,执行每个类的filter_queryset进行筛选
    • 执行

      paginate_queryset
      

      对queryset进行分页:

      • 读取配置文件中的分页类
      • 执行分页类的paginate_queryset函数
      • 在函数中读取配置文件中的默认显示数和url中的页码
      • 通过显示数和页码对quertset进行分页

    4.将结果封装到response中进行返回

  • 相关阅读:
    从目标检测到图像分割简要发展史
    用卷积神经网络和自注意力机制实现QANet(问答网络)
    C# 监测每个方法的执行次数和占用时间(测试5)
    C# list常用的几个操作 改变list中某个元素的值 替换某一段数据
    C# 监测每个方法的执行次数和占用时间(测试4)
    生活 对最近工作 和 生活 感悟
    Cesium 加载天地图
    其它 开发常用工具
    PowerDesigner 画流程图
    RookeyFrame bin 目录
  • 原文地址:https://www.cnblogs.com/zgboy/p/13775596.html
Copyright © 2011-2022 走看看