zoukankan      html  css  js  c++  java
  • django 之(二) --- 源码分析

    CBV类视图继承

    • CBV:继承自View;注册的时候使用的as_view()

    • 入口

      • 不能使用请求方法的名字作为参数的名字

      • 只能接受已经存在的属性对应的参数

      • 定义了一个view

        • 创建了一个类视图对象

        • 保留,拷贝传递进来的属性和参数

        • 调用dispatch方法

          • 分发

          • 如果请求方法在我们的允许的列表中

            • 从自己这个对象中获取请求方法名字小写对应的属性,如果没有找到,会给一个默认http_method_not_allowded

          • 如果请求方法不在我们允许的列表中,直接就是http_method_not_allowed

          • 之后将参数传递,调用函数

    • 默认实现了options

      • 获取接口信息,可以获取接口都允许什么请求

    • 类视图继承自View;注册时使用as_view()
    1 from django.conf.urls import url
    2 from CBV import views
    3 
    4 urlpatterns = [
    5     url(r'^hello/', views.HelloCBV.as_view(msg='Sleeping'), name='hello'),
    6     url(r'^books/', views.BooksCBV.as_view(), name='books'),
    7 ]
    • as_view 源码(base.py文件中)  
    • 流程:as_view --> dispatch分发 --> 调用实现请求方法对应的函数名
     1 class View(object):
     2     """
     3     Intentionally simple parent class for all views. Only implements
     4     dispatch-by-method and simple sanity checking.
     5     """
     6    # 允许的请求方法 
     7     http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
     8 
     9     def __init__(self, **kwargs):
    10         """
    11         Constructor. Called in the URLconf; can contain helpful extra
    12         keyword arguments, and other things.
    13         """
    14         # Go through keyword arguments, and either save their values to our
    15         # instance, or raise an error.
    16         for key, value in six.iteritems(kwargs):
    17             setattr(self, key, value)
    18 
    19     @classonlymethod
    20     def as_view(cls, **initkwargs):
    21         """
    22         Main entry point for a request-response process.
    23         """
    24         for key in initkwargs:
              # 判断,不可以使用请求方法的名字作为参数名字
    25 if key in cls.http_method_names: 26 raise TypeError("You tried to pass in the %s method name as a " 27 "keyword argument to %s(). Don't do that." 28 % (key, cls.__name__))
              # 只能接收类视图中已经存在的属性作为参数名(路由中的变量名要和视图函数中的属性名相同)
    29 if not hasattr(cls, key): 30 raise TypeError("%s() received an invalid keyword %r. as_view " 31 "only accepts arguments that are already " 32 "attributes of the class." % (cls.__name__, key)) 33      # 定义了一个view 34 def view(request, *args, **kwargs): 35 self = cls(**initkwargs) 36 if hasattr(self, 'get') and not hasattr(self, 'head'): 37 self.head = self.get # 默认get请求和head请求方式一样;支持get就支持head请求 38 self.request = request # 将请求的参数,属性记录下来(三行) 39 self.args = args 40 self.kwargs = kwargs
              # 返回时调用dispatch方法
    41 return self.dispatch(request, *args, **kwargs) 42 view.view_class = cls 43 view.view_initkwargs = initkwargs 44 45 # take name and docstring from class 46 update_wrapper(view, cls, updated=()) 47 48 # and possible attributes set by decorators 49 # like csrf_exempt from dispatch 50 update_wrapper(view, cls.dispatch, assigned=()) 51 return view 52

    53 def dispatch(self, request, *args, **kwargs): 54 # Try to dispatch to the right method; if a method doesn't exist, 55 # defer to the error handler. Also defer to the error handler if the 56 # request method isn't on the approved list.
           # 将请求方法名变为小写,并判断请求方法名是否在允许的请求列表中 57 if request.method.lower() in self.http_method_names:
              # 如果在允许的请求列表中,从自己的对象中去获取请求方法名字小写的属性,没有找到给变量一个默认值
    58 handler = getattr(self, request.method.lower(), self.http_method_not_allowed) 59 else: # 如果请求方法不在被允许的请求列表中,则http_method_not_allowd给变量 60 handler = self.http_method_not_allowed 61 return handler(request, *args, **kwargs) 62 63 def http_method_not_allowed(self, request, *args, **kwargs): 64 logger.warning( 65 'Method Not Allowed (%s): %s', request.method, request.path, 66 extra={'status_code': 405, 'request': request} 67 ) 68 return http.HttpResponseNotAllowed(self._allowed_methods()) 69
        # 获取接口信息,获取接口都允许接收什么请求 70 def options(self, request, *args, **kwargs): 71 """ 72 Handles responding to requests for the OPTIONS HTTP verb. 73 """ 74 response = http.HttpResponse() 75 response['Allow'] = ', '.join(self._allowed_methods()) 76 response['Content-Length'] = '0' 77 return response 78 79 def _allowed_methods(self): 80 return [m.upper() for m in self.http_method_names if hasattr(self, m)] 

    类视图子类

    [这些子类和模版是高度耦合的,在前后端分离中不会被使用。Mixin:只要继承中有此单词,就表示混合多继承]

    TemplateView是多继承的子类,继承自:

    • View:分发函数:dispatch
    • ContextMixin:接收上下文(从视图函数传递到模板的内容)。函数:get_context_data
    • TemplateResponseMixin:将内容渲染到模板中。属性:template_name、template_engine、response_class、content_type。函数:render_to_response

    base.py 源码分析

    1 # TemplateView
    2 class TemplateView(TemplateResponseMixin, ContextMixin, View):
    3     """
    4     A view that renders a template.  This view will also pass into the context
    5     any keyword arguments passed by the URLconf.
    6     """
    7     def get(self, request, *args, **kwargs):
    8         context = self.get_context_data(**kwargs)
    9         return self.render_to_response(context)

    views.py 使用

    1 from django.views.generic import TemplateView
    2 
    3 # CBV继承TemplateView,实现了get请求继承自:TemplateResponseMixin, ContextMixin, View(实现请求分发)
    5 class HelloTemplateView(TemplateView):
    6     template_name = 'hello.html'

     ListView是多继承子类。继承自:

    • MultipleObjectTemplateResponseMixin

      • TemplateResponseMixin

      • 获取模板名字,首先根据template_name获取;如果没找到,自己根据应用的名字,关联模型的名字,_list.html 去查找,App/book_list.html

    • BaseListView

      • MultipleObjectMixin

        • ContextMixin、get_queryset、model

      • View:默认实现了get,渲染成了response

    views.py 使用

    1 from django.views.generic import ListView
    2 from App.models import Book
    3 
    4 # CBV继承ListView,实现了get请求。获取一个集合数据
    5 # ListView继承自:MultipleObjectTemplateResponseMixin, BaseListView
    6 class HelloListView(ListView):
    7     template_name = 'BookList.html'
    8     model = Book

    book.html

    1 <body>
    2     <ul>
    3         {% for book in object_list %}
    4             <li><a href="{% url 'cbv:single' pk=book.id %}">{{ book.b_name }}</a></li>
    5         {% endfor %}
    7     </ul>
    8 </body>

    DetailView是多继承子类。继承自:

    • SingleObjectTemplateResponseMixin
      • TemplateResponseMixin:重写了获取模板名字的方法
    • BaseDetailView
      • View、SingleObjectMixin

    views.py 使用

    1 from django.views.generic import DetailView
    2 from App.models import Book
    3 
    4 # CBV继承DetailView。继承自SingleObjectTemplateResponseMixin, BaseDetailView
    5 class HeDetailView(DetailView):
    6     # template_name = 'Book.html'  #. 如果不指定模版,自动去查找名字为:book_detail.html的模版
    7     # model = Book
    8     queryset = Book.objects.all()

    urls.py

    1 from django.conf.urls import url
    2 from App import views
    3 
    4 urlpatterns = [ # 单一实例:pk、slug
    5     url(r'^single/(?P<pk>d+)/', views.HeDetailView.as_view(), name='single'),
    6 ]




    APIView源码分析 

    APIView源码封装的类及用途

    • renderer_classes:            渲染的类
    • parser_classes:               解析转换的类
    • authentication_classes:    认证的类
    • throttle_classes:              节流的类、控制请求频率的[此接口每分钟请求多少次]
    • permission_classes:         权限的类
    • content_negotiation_class:内容过滤类 
    • metadata_class:              元信息的类
    • versioning_class:             版本控制的类
    • as_view( )方法:调用父类中的as_view。对dispatch重写,主要做dispatch分发。
      • 重写的dispatch方法中的 initialize_request 方法初始化一个新的request。
      • 使用django的request构建了一个REST中的Request,
      • 将Django中的Request作为了自己的一个私有属性 _request
      • (若在rest_formwork中获取django的request,需要request._reuqest)
        • initial  初始化。获取接受的渲染、获取接受的类型、版本是否支持
          • perform_authentication:
            • 执行用户认证。request.user[.user中将方法改为属性,并进行认证]
              • 遍历用户认证器,如果认证成功会返回一个元组
              • 元组中的第一个元素就是 user
              • 第二个元素就是 auth [token]
          • check_permissions
            • 检查用户权限
              • 遍历权限检测器
              • 只要有一个权限检测没通过就直接显示权限被拒绝
              • 只有所有权限都满足,才算是拥有权限
          • check_throttles
            • 检测访问频率
              • 遍历频率限制器
              • 如果验证不通过,就需要等待
    • csrf_exempt
      • 所有APIView的子类都是csrf豁免的

    Request源码分析

    • Request

      • 它是rest_framework的request。(rest_framework.request)

      • 将Django中的Request作为了自己的一个属性 _request

      • 属性和方法

        • content_type: 传输内容的类型

        • stream:          流

        • query_params:查询参数。[可以在post请求中获取GET参数][将Django中的request.GET方法改名为query_params]

        • data:              处理任意数据,同时兼容 POST,PUT,PATCH

        • user:              可以直接在请求上获取用户。[相当于在请求上添加一个用户对象属性]

        • auth:              认证。相当于请求上添加了一个属性,属性值是token

        • successful_authenticator:认证成功

    Response源码分析

    • Response

      • 依然是HttpResponse的子类。只要类视图继承自APIView后,

      • Response自己封装的data 直接接受字典转换成JSON [ Response(data=request.data, status(状态码)=201) ]

      • 属性和方法

        • rendered_content:渲染的内容

        • status_text:状态码转成常量

          • 封装 status模块中,实际上就是一个常量类

    针对视图函数的包装

      REST框架提供了两种可用于编写API视图的包装器(wrappers)

      • CBV:APIView
      • FBV:添加 @api_view装饰器;必须手动指定允许的请求方法
        • @api_view(http_method_names=['GET', 'POST'])
     1 from rest_framework import status
     2 from rest_framework.decorators import api_view
     3 from rest_framework.response import Response
     4 from RestSerializers.serializers import BookSerializer
     5 
     6 
     7 @api_view(http_method_names=['GET', 'POST'])
     8 def books(request):
     9     print(type(request))
    10     if request.method == "GET":
    11         return Response(data={"msg": "get ok"})
    12     elif request.method == "POST":
    13         print(request.data)
    14         book_serializer = BookSerializer(data=request.data)
    15         if book_serializer.is_valid():
    16             book_serializer.save()
    17             return Response(data=book_serializer.data)
    18         return Response(data={'msg': 'error'}, status=status.HTTP_400_BAD_REQUEST)


    APIView类视图子类

    GenericAPIView继承自APIView;GenericAPIView是一个基类

    • generics包中。子类:

      • GenericAPIView

        • 增加的模型的获取操作

        • get_queryset:获取查询结果集

        • get_object:   获取单个对象。lookup_field 默认pk

        • get_serializer:序列化实例

        • get_serializer_class:     获取序列化类

        • get_serializer_context:  获取序列化上下文内容

        • filter_queryset:             对查询结果集过滤

        • paginator:                直接构件的分页器

        • paginate_queryset:        对查询结果集分页

        • get_paginated_response:获取分页后的结果

      • CreateAPIView

        • 创建的类视图。实现了post进行创建

        • 继承自GenericAPIView

        • 继承自CreateModelMixin

      • ListAPIView

        • 列表的类视图。实现了get

        • 继承自GenericAPIView

        • 继承自ListModelMixin

      • RetrieveAPIView

        • 查询单个数据的类视图。实现了get

        • 继承自GenericAPIView

        • 继承自RetrieveModelMixin

      • DestroyAPIView

        • 销毁数据的类视图,删除数据的类视图。实现了delete

        • 继承自GenericAPIView

        • 继承自DestroyModelMixin

      • UpdateAPIView

        • 更新数据的类视图。实现了put、patch

        • 继承自GenericAPIView

        • 继承自UpdateModelMixin

      • ListCreateAPIView

        • 获取列表数据,创建数据的类视图。实现了get、post

        • 继承自GenericAPIView

        • 继承自ListModelMixin

        • 继承自CreateModelMixin

      • RetrieveUpdateAPIView

        • 获取单个数据,更新单个数据的类视图。实现了get、put、patch

        • 继承自GenericAPIView

        • 继承自RetrieveModelMixin

        • 继承自UpdateModelMixin

      • RetrieveDestroyAPIView

        • 获取单个数据,删除单个数据。实现了get、delete

        • 继承自GenericAPIView

        • 继承自RetrieveModelMixin

        • 继承自DestroyModelMixin

      • RetrieveUpdateDestroyAPIView

        • 获取单个数据,更新单个数据,删除单个数据的类视图。实现了get、put、patch、delete

        • 继承自GenericAPIView

        • 继承自RetrieveModelMixin

        • 继承自UpdateModelMixin

        • 继承自DestroyModelMixin

    • mixins

      • CreateModelMixin

        • create

        • perform_create

        • get_success_headers

      • ListModelMixin

        • list:查询结果集,添加分页,帮你序列化

      • RetrieveModelMixin

        • retrieve:获取单个对象并进行序列化

      • DestroyModelMixin

        • destroy:

          • 获取单个对象

          • 调用执行删除

          • 返回Respon 状态码204

        • perform_destroy

          • 默认是模型的delete

          • 如果说数据的逻辑删除。重写进行保存

      • UpdateModelMixin

        • update:获取对象,合法验证;执行更新

        • perform_update

        • partial_update:差量更新,对应的就是patch

    • viewsets继承自:

      • ViewSetMixin:

        • 重写as_view。添加过滤和反向解析

      • GenericViewSet

        • 继承自GenericAPIView

        • 继承自ViewSetMixin

      • ViewSet

        • 继承自APIView

        • 继承自ViewSetMixin

        • 默认啥都不支持,需要自己手动实现

      • ReadOnlyModelViewSet

        • 只读的模型的视图集合

        • 继承自RetrieveModelMixin

        • 继承自ListModelMixin

        • 继承自GenericViewSet

      • ModelViewSet

        • 直接封装对象的所有操作

        • 继承自GenericViewSet

        • 继承自CreateModelMixin

        • 继承自RetrieveModelMixin

        • 继承自UpdateModelMixin

        • 继承自DestroyModelMixin

        • 继承自ListModelMixin

    封装使用1:

    • user/serializers.py 
    1 from rest_framework import serializers
    2 from user.models import User
    3 # ModelSerializer没有超链接的序列化
    4 class SingleUserSerializer(serializers.ModelSerializer):
    5     class Meta:
    6         model = User
    7         fields = ['id', 'username', 'password', 'phone',]
    • user/models.py
     1 from django.db import models
     2 
     3 class User(models.Model):
     4     username = models.CharField(max_length=20, unique=True)
     5     password = models.CharField(max_length=128)
     6     phone = models.CharField(max_length=11)
     7     add_time = models.DateTimeField(auto_now=True)
     8 
     9     class Meta:
    10         db_table = 'user'
    11 
    12     def __str__(self):
    13         return self.username
    • user/views.py
    from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView
    from user.models import User
    from user.serializers import SingleUserSerializer
    
    class UserResouce(ListCreateAPIView):
        # 操作的模型
        queryset = User.objects.all()
        # 此模型对应的序列化类
        serializer_class = SingleUserSerializer
    
    class UserResourcePro(RetrieveUpdateDestroyAPIView):
        queryset = User.objects.all()
        serializer_class = SingleUserSerializer
    • user/urls.py
    1 from django.urls import path
    2 from user.views import UserResouce, UserResourcePro 
    3 
    4 app_name = 'user'
    5 urlpatterns = [
    6     path('single', UserResouce.as_view(), name='single'),
    7     path('singlepro/<int:pk>', UserResourcePro.as_view(), name='singlepro'),
    8 ]

    终极封装:

    • user/views.py
    1 from rest_framework.viewsets import ModelViewSet
    2 from user.models import User
    3 from user.serializers import SingleUserSerializer
    4 
    5 class UserResourceVPro(ModelViewSet):
    6     queryset = User.objects.all()
    7     serializer_class = SingleUserSerializer
    • user/urls.py
     1 from django.urls import path, include
     2 from rest_framework.routers import DefaultRouter
     3 from user.views import UserResourceVPro
     4 
     5 router = DefaultRouter()
     6 router.register(r'single', UserResourceVPro)
     7 # 此方式也可以获取单个对象:http://127.0.0.1:8000/user/single/2/
       # 主路由中
    8
    urlpatterns = [ 9 path('', include(router.urls)) 10 ]

      封装使用2:

    • urls.py
    1 from django.conf.urls import url, include
    2 from django.contrib import admin
    3 from App.urls import router
    4 
    5 urlpatterns = [
    6     url(r'^admin/', admin.site.urls),
    7     url(r'^app/', include('App.urls')),
    8     url(r'^app/', include(router.urls)),
    9 ]
    • App/urls.py
     1 from django.conf.urls import url
     2 from rest_framework.routers import DefaultRouter
     3 from App import views
     4 from App.views import GameModelViewSet
     5 
     6 urlpatterns = [
     7     url(r'^games/$', views.GamesView.as_view()),
     8     url(r'^games/(?P<pk>d+)/$', views.GameView.as_view(), name='game-detail'),
     9 ]
    10 
    11 router = DefaultRouter()
    12 router.register(r'progames', GameModelViewSet)
    • App/models.py
    1 from django.db import models
    2 
    3 class Game(models.Model):
    4     g_name = models.CharField(max_length=32)
    5     g_price = models.FloatField(default=0)
    • App/serializers.py
    1 from rest_framework import serializers
    2 from App.models import Game
    3 # HyperlinkedModelSerializer带超链接的序列化
    4 class GameSerializer(serializers.HyperlinkedModelSerializer):
    5 
    6     class Meta:
    7         model = Game
    8         fields = ('url', 'id', 'g_name', 'g_price')
    • App/views.py
     1 from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView
     2 from rest_framework.viewsets import ModelViewSet
     3 from App.models import Game
     4 from App.serializers import GameSerializer
     5 
     6 
     7 class GamesView(ListCreateAPIView):
     8     serializer_class = GameSerializer
     9     queryset = Game.objects.all()
    10 
    11 
    12 class GameView(RetrieveUpdateDestroyAPIView):
    13     serializer_class = GameSerializer
    14     queryset = Game.objects.all()
    15 
    16 
    17 class GameModelViewSet(ModelViewSet):
    18     serializer_class = GameSerializer
    19     queryset = Game.objects.all()
    生如逆旅 一苇以航
  • 相关阅读:
    Django的filter查询
    第一篇博客
    算法:位运算加减乘除
    ip头、tcp头、udp头详解及定义,结合Wireshark抓包看实际情况
    linux ls -l 详解
    重要常用的Lunix命令
    开博感言(真正第一篇博客园博客)
    校招与内推:软实力的技巧
    最长公共子串——动态规划求解
    最长公共子序列——动态规划求解
  • 原文地址:https://www.cnblogs.com/TMMM/p/11913341.html
Copyright © 2011-2022 走看看