zoukankan      html  css  js  c++  java
  • drf安装和CBV源码剖析

    1.drf的安装

      安装:pip install djangorestframework==3.10.3

      使用:

    1 在setting.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中测试即可

    2.CBV中View源码

      ModelViewSet继承View(django原生View),APIView继承了View,因此我们先来读View的源码

      1.由于CBV在路由中的写法是views.xxx.as_view(),所以先运行的是as_view()方法(部分代码已删除)

    @classonlymethod #装饰器继承了classmethod,自动将调用的类传入
        def as_view(cls, **initkwargs):
            .........
            return view    #返回的是一个方法(函数内存地址)
    #views.xxx.as_view()执行完,是个函数内存地址,as_view是一个类方法,类直接来调用,会把类自动传入,放了一个view的内存地址(View--》as_view--》内层函数)

      2.as_view方法执行完毕后由于返回的是一个view的函数内存地址,所以会执行as_view中的view方法

    def view(request, *args, **kwargs): #request指的是该次请求的request,是Django自动给你传入的
      self = cls(**initkwargs)  #实例化对象
        if hasattr(self, 'get') and not hasattr(self, 'head'):#用过反射判断出对象中是否存在get方法
          self.head = self.get
        self.request = request
        self.args = args
        self.kwargs = kwargs
      return self.dispatch(request, *args, **kwargs)   #返回dispatch方法

      3. 先到自己写的类中找dispath方法,没有再去父类(view)中找dispath并执行

    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
    def dispatch(self, request, *args, **kwargs):
      #request是该次请求的request,self是该次调用的类(xxx)
      if request.method.lower() in self.http_method_names:#将请求的方法转化为小写并判断是否在 http_method_names里面
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)#self指的是调用的类, request.method.lower()本次的请求方式,这句话等同于handler=getattr(self,request.method.lower()),而handler现在指的是你的类方法中写的请求方法的内存地址。如xxx类中的get方法的内存地址
      else:
        handler = self.http_method_not_allowed
      return handler(request, *args, **kwargs)    #执行get(request)

     3.APIView源码

    from rest_framework.views import APIView
    
    # urls.py
    path('booksapiview/', views.BooksAPIView.as_view()),  #在这个地方应该写个函数内存地址

      1.APIView继承了View,最先执行的也是as_view()

    @classmethod
    def as_view(cls, **initkwargs):
        view = super().as_view(**initkwargs)  #调用父类(View)的as_view(**initkwargs)
        view.cls = cls
        view.initkwargs = initkwargs
        return csrf_exempt(view)#局部取消csrf验证  只要继承了APIView,就没有csrf的认证

      2.请求来了--->路由匹配上--->view(request)--->调用了self.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    
    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)
    APIView的initial方法

      3. 分析下新的request对象

    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(包括文件,json等)

      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)

    补充

      由于一切皆对象

    def foo(a,b):
        return a+b
    foo.name='lqz'  #由于一切皆对象,函数也是个对象,对象放值
    print(foo(2,3))
    print(foo.name)

      上述源码中有许多地方也都用到了这个点

  • 相关阅读:
    作业12:字典dict讲解及增删改查等操作
    作业11:元祖及元祖的嵌套
    作业10:列表的嵌套
    作业09:列表的增删改查
    什么数据类型
    作业08:字符串操作
    Visual Studio Code 写Python 代码
    Python——面向对象(初级篇)
    Python 学习第三部分函数——第一章函数基础
    Python3 字典
  • 原文地址:https://www.cnblogs.com/bk134/p/13256423.html
Copyright © 2011-2022 走看看