zoukankan      html  css  js  c++  java
  • 一、drf入门规范

    drf入门规范

    一、Web一能用模式

    • 前后端混合开发(前后端不分离:全栈):返回来的是html的内容,需要自己写模板
    • 前后端分离:只专注于写后端接口,返回的是json、xml(过时)格式的数据
    • 前后端分离需要中间媒介:api接口

    二、API接口

    • 通过网络,规定前后台信息交互规则的url链接
    • 就是前后端交互的媒介,双方协议好传输数据的格式

    三、Restful规范(重点)

    # REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征性状态转移)。 它首次出现在2000年Roy Fielding的博士论文中。
    
    # uri:资源唯一标识(抽象概念)
    # url:资源唯一定位(具体实现)
    

    RESTful是Web API接口的设计规范风格,这种风格的理念认为后端开发任务就是提供数据的,对外提供的是数据资源的访问接口,尤其适用于前后端分离的应用模式中。

    所以在定义接口时,客户端访问的URL路径就表示这种要操作的数据资源。

    其实,任何一个框架都可以实现符合restful规范的API接口。

    十条规范

    1.数据的安全保障:
    	url链接一般都是采用哪个https协议进行传输
        注:采用https协议,可以提高数据交互过程中的安全性
    2.接口特征表现,一看看就知道是以api接口
    	用api关键字标识接口url
        	https://www.baidu.com/api
    		[https://api.baidu.com](https://api.baidu.com/)
        注:看到api字眼,就代表该请求url链接是完成前后台数据交互的
    3.多数据版本共存
    	在url链接中标识数据版本
        	https://api.baidu.com/v1
        	https://api.baidu.com/v2
        注:url链接中的v1、v2就是不同数据版本的体现(只有在一种数据资源有多版本情况下)
    4 数据即是资源,均使用名词(可复数)#####################重点####################
    	接口一般都是完成前后台数据的交互,交互的数据我们称之为资源
        	https://api.baidu.com/users
        注:一般提倡用资源的复数形式,在url链接中奖励不要出现操作资源的动词
        特殊的接口可以出现动词,因为这些接口一般没有一个明确的资源,或是动词就是接口的核心含义
            https://api.baidu.com/place/search
            https://api.baidu.com/login
    5 资源操作由请求方式决定(method)#####################重点####################
         操作资源一般都会涉及到增删改查,使用请求方式确定操作类型
         	https://api.baidu.com/books - get请求:获取所有书
          	https://api.baidu.com/books/1 - get请求:获取主键为1的书
          	https://api.baidu.com/books - post请求:新增一本书书
          	https://api.baidu.com/books/1 - put请求:整体修改主键为1的书
          	https://api.baidu.com/books/1 - patch请求:局部修改主键为1的书
          	https://api.baidu.com/books/1 - delete请求:删除主键为1的书
    6 过滤,通过在url上传参的形式传递搜索条件,或者说查询参数(query_params)
        - https://api.example.com/v1/zoos?limit=10:指定返回记录的数量
        - https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置
        - https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
        - https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
        - https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件
            
    7 响应状态码
       7.1 正常响应
        - 响应状态码2xx
          - 200:常规请求
          - 201:创建成功
       7.2 重定向响应
        - 响应状态码3xx
          - 301:永久重定向
          - 302:暂时重定向
       7.3 客户端异常
        - 响应状态码4xx
          - 403:请求无权限
          - 404:请求路径不存在
          - 405:请求方法不存在
    	7.4 服务器异常
        - 响应状态码5xx
          - 500:服务器异常
            
     8 错误处理,应返回错误信息,error当做key
        {
            error: "无权限操作"
        }
        
     9 返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范
        GET /collection:返回资源对象的列表(数组)
        GET /collection/resource:返回单个资源对象
        POST /collection:返回新生成的资源对象
        PUT /collection/resource:返回完整的资源对象
        PATCH /collection/resource:返回完整的资源对象
        DELETE /collection/resource:返回一个空文档
        
     10 需要url请求的资源需要访问资源的请求链接
        - Hypermedia API,RESTful API最好做到Hypermedia,
        - 即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么
            {
                "status": 0,
                "msg": "ok",
                "results":[
                    {
                        "name":"肯德基(罗餐厅)",
                        "img": "https://image.baidu.com/kfc/001.png"
                    }
                    ...
                    ]
            }
    

    四、drf的安装和简单使用

    • 安装:pip install djangorestframework==3.10.3

    • 使用

      1. 在settings.py的app中注册

        INSTALLED_APPS = [
           'rest_framework'
        ]
        
      2. 在models.py中写表模型

        class Book(models.Model):
            nid=models.AutoField(primary_key=True)
            name=models.CharField(max_length=32)
            price=models.DecimalField(max_digits=5,decimal_places=2)
            author=models.CharField(max_length=32)
        
      3. 新建一个序列化类(听不懂)

        from rest_framework.serializers import ModelSerializer
        from app01.models import  Book
        class BookModelSerializer(ModelSerializer):
            class Meta:
                model = Book
                fields = "__all__"
        
      4. 在视图中写视图类

        from rest_framework.viewsets import ModelViewSet
        from .models import Book
        from .ser import BookModelSerializer
        class BooksViewSet(ModelViewSet):
            queryset = Book.objects.all()
            serializer_class = BookModelSerializer
        
      5. 写路由关系

        from app01 import views
        from rest_framework.routers import DefaultRouter
        router = DefaultRouter()  # 可以处理视图的路由器
        router.register('book', views.BooksViewSet)  # 向路由器中注册视图集
        # 将路由器中的所以路由信息追到到django的路由列表中
        urlpatterns = [
            path('admin/', admin.site.urls),
        ]
        #这是什么意思?两个列表相加
        # router.urls  列表
        urlpatterns += router.urls
        
      6. 启动,在postman中测试即可

    五、CBV源码阅读

    • 代码分析

      1. 切入点: urls.py中的.as_view()方法

        url(r'^books1/', views.Books.as_view())
        
      2. 视图中Books类

        class Books(View):
            # 如果有个需求,该视图只能接受get请求?
            # 重写http_method_names列表,只保留get请求即可
            http_method_names = ['get']
            
            def get(self,request):
                print(self.request) # 这里可以通过self.request获取到request对象
                return HttpResponse("ok")        
        
      3. 项目启动时执行

        @classonlymethod # 就是classmethod装饰器,django重写了classmethod而已
        def as_view(cls, **initkwargs):
        	...
        
            def view(request, *args, **kwargs):
                ...
        
            ...
            return view
        
      4. 路由匹配时执行, 将执行的类进行实例化, 通过执行路由匹配中类实例化出的对象就可以执行到继承父类View中定义的dispatch方法

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
                self.request = request
                self.args = args
                self.kwargs = kwargs
                return self.dispatch(request, *args, **kwargs)
        
      5. 通过dispatch方法中使用反射获取请求的类型去执行对应类中定义的请求的方法

        def dispatch(self, request, *args, **kwargs):
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(), self.http_method_not_allowed) # 使用反射
            else:
                handler = self.http_method_not_allowed
            return handler(request, *args, **kwargs) # 直接加括号调用handler
        
      6. 上面的self.http_method_names判断, 如果子类中没有派生那么就会通过View中定义的进行判断

        class View(object):
            http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
            ...
        
    • 图示分析

      1. 切入点: urls.py中的.as_view()方法image-20201227201430401

      2. 视图中Books类

        image-20201227201636023

      3. 项目启动时执行

        image-20201227201824849

      4. 路由匹配时执行, 将执行的类进行实例化, 通过执行路由匹配中类实例化出的对象就可以执行到继承父类View中定义的dispatch方法

        image-20201227202012642

      5. 通过dispatch方法中使用反射获取请求的类型去执行对应类中定义的请求的方法

        image-20201227202159850

      6. 上面的self.http_method_names判断, 如果子类中没有派生那么就会通过View中定义的进行判断

        image-20201227202236755

    • 总结

      1. 项目启动: views.Books.as_view()就是View类中的view函数内存地址
      2. 路由匹配: 将view加括号调用, 并传入request对象.
      3. 执行view: 将Books类进行实例化, 实例化出Books类的对象. 这里的self就是Books类实例化出的对象
      4. 执行self.dispatch方法: self就是Books类实例化出的对象, 如果之类以及对象中都没有定义, 就会执行View类中的dispatch方法
      5. dispatch方法中就是通过用户当前的请求的小写字符串进行反射. 当继承View的类中有get方法, 根据对象属性查找顺序就会调用子类中定义的get方法

    六、drf中的APIView源码阅读

    • 代码分析

      1. 切入点: urls.py中的.as_view()方法

        url(r'^booksapiview/', views.BooksAPIView.as_view()),
        
      2. 视图中BooksAPIView类

        class BooksAPIView(APIView):
            def get(self, request):
                # request 已经不是原生django的request了,是drf自己定义的request对象
                # print(request._request)
                # print(request.data)
                print(request.method)
                print(request.query_params)  # get请求,地址中的参数
                print(request.GET)
                return HttpResponse('ok')
        
            def post(self, request):
                print(request.data)  # urlencoded方式有数据,json格式也有,formdata也有数据
                print(type(request.POST))  # 原生的POST
                print(request._request.POST)
        
                return HttpResponse('ok')
        
      3. 项目启动时执行

        @classmethod 
        def as_view(cls, **initkwargs):
            ···
            view = super().as_view(**initkwargs) # 调用父类的as_view方法
            view.cls = cls # cls就是BooksAPIView
            return csrf_exempt(view) # 整个网站就取消的csrf的验证
        
      4. 执行APIView父类(View)的as_view方法,返回父类的view方法

        @classonlymethod
        def as_view(cls, **initkwargs):
            ···
            def view(request, *args, **kwargs):
                ···
                return self.dispatch(request, *args, **kwargs)
            return view
        
      5. 执行父类的view方法,并且调用dispatch方法,但是APIView自己写了一个dispatch方法,所以不会执行父类的dispatch方法

        # APIView的dispatch方法
            def dispatch(self, request, *args, **kwargs):
                self.args = args
                self.kwargs = kwargs
                # 重新包装成一个request对象,以后再用的request对象,就是新的request对象了
                request = self.initialize_request(request, *args, **kwargs)
                self.request = request
                self.headers = self.default_response_headers  # deprecate?
                try:
                    # 三大认证模块
                    self.initial(request, *args, **kwargs)
                    # Get the appropriate handler method
                    if request.method.lower() in self.http_method_names:
                        handler = getattr(self, request.method.lower(),
                                          self.http_method_not_allowed)
                    else:
                        handler = self.http_method_not_allowed
                    # 响应模块
                    response = handler(request, *args, **kwargs)
                except Exception as exc:
                    # 异常模块
                    response = self.handle_exception(exc)
                # 渲染模块
                self.response = self.finalize_response(request, response, *args, **kwargs)
                return self.response
        
      6. APIView自己封装的request对象方法initialize_request()方法

        def dispatch(self, request, *args, **kwargs):
        	···
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request # 已不再是django原来的request对象了
        
            
        def initialize_request(self, request, *args, **kwargs):
            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
            )
        
      7. APIView自己的认证方法initial()方法

        def dispatch(self, request, *args, **kwargs):
            ···
            try:
                    # 三大认证模块
                self.initial(request, *args, **kwargs)
                # 和view里面的dispatch中的使用反射代码一致
                if request.method.lower() in self.http_method_names:
                	handler = getattr(self, request.method.lower(),
                                          self.http_method_not_allowed)
                else:
                    handler = self.http_method_not_allowed
                    # 响应模块
                response = handler(request, *args, **kwargs)
            except Exception as exc:
                ···
        
        # APIView的initial方法
        def initial(self, request, *args, **kwargs):
            # 认证组件:校验用户 - 游客、合法用户、非法用户
            # 游客:代表校验通过,直接进入下一步校验(权限校验)
            # 合法用户:代表校验通过,将用户存储在request.user中,再进入下一步校验(权限校验)
            # 非法用户:代表校验失败,抛出异常,返回403权限异常结果
            self.perform_authentication(request)
            # 权限组件:校验用户权限 - 必须登录、所有用户、登录读写游客只读、自定义用户角色
            # 认证通过:可以进入下一步校验(频率认证)
            # 认证失败:抛出异常,返回403权限异常结果
            self.check_permissions(request)
            # 频率组件:限制视图接口被访问的频率次数 - 限制的条件(IP、id、唯一键)、频率周期时间(s、m、h)、频率的次数(3/s)
            # 没有达到限次:正常访问接口
            # 达到限次:限制时间内不能访问,限制时间达到后,可以重新访问
            self.check_throttles(request)
        
      8. 继承APIView视图的类中含有的方法分析:

        • django drf
          request _request
          FILES FILES
          GET query_params
        • drf中的data对标django中的request,但是django中的request只能接收urlencoded和form-data格式的数据,drf中的data能接收urlencoded、form-data和json格式的数据

        from rest_framework.request import Request
        # 只要继承了APIView,视图类中的request对象,都是新的,也就是上面那个request的对象了
        # 老的request在新的request._request
        # 以后使用reqeust对象,就像使用之前的request是一模一样(因为重写了__getattr__方法)
          def __getattr__(self, attr):
                try:
                    return getattr(self._request, attr) #通过反射,取原生的request对象,取出属性或方法
                except AttributeError:
                    return self.__getattribute__(attr)
        
         # request.data 感觉是个数据属性,其实是个方法,@property,修饰了
        	它是一个字典,post请求不管使用什么编码,传过来的数据,都在request.data
         #get请求传过来数据,从哪取?
        	request.GET
            @property
            def query_params(self):
                """
                More semantically correct name for request.GET.
                """
                return self._request.GET
            
            #视图类中
             print(request.query_params)  #get请求,地址中的参数
             # 原来在
             print(request.GET)
        
    • 图示分析

      1. 切入点: urls.py中的.as_view()方法

        image-20201227211143295

      2. 视图中BooksAPIView类

        image-20201227211211468

      3. 项目启动时执行

        image-20201227211718502

      4. 执行APIView父类(View)的as_view方法,返回父类的view方法

        image-20201227212237716

      5. 执行父类的view方法,并且调用dispatch方法,但是APIView自己写了一个dispatch方法,所以不会执行父类的dispatch方法

        image-20201227212204787

      6. APIView自己封装的request对象方法initialize_request()方法

        image-20201227212408525

      7. APIView自己的认证方法initial()方法

        image-20201227212700980

      8. 继承APIView视图的类中含有的方法分析:

        • data,原装的request == _request

          image-20201227213015905

        • query_params,原装的GET == _request.GET

          image-20201227213134927

        • FILES

          image-20201227213255068

    七、拓展

    • 一切皆对象,函数也是对象

      def foo(a, b):
          return a + b
      foo.name = 'lqz'  # 由于一切皆对象,函数也是个对象,对象放值
      
      print(foo(2, 3))
      
      print(foo.name)  # lqz
      
    • 局部禁用csrf方法和csrf_exempt

      # 提示: 在视图函数上加装饰器@csrf_exempt. 与csrf_exempt(view)这么写和在视图函数上加装饰器是一模一样的.
      
      # urls.py中这种写法本质就是返回的views.test, 只是在原来基础之上不修改的调用方式, 以及原代码的情况下, 去除了csrf认证
      path('test/', csrf_exempt(views.test)),
      
  • 相关阅读:
    java操作docker示例(docker-java)
    istio实现对外暴露服务
    istio实现自动sidecar自动注入(k8s1.13.3+istio1.1.1)
    k8s1.13.3安装istio(helm方式)
    wrk http压测工具介绍
    etcd 相关介绍
    openresty 常用API学习
    Lua 相关知识点
    Lua 获取table的长度
    Lua 字符串相关操作
  • 原文地址:https://www.cnblogs.com/borntodie/p/14330720.html
Copyright © 2011-2022 走看看