REST framework 请求模块

问题:为什么前端的请求过来能自动识别Django的CBV模式里的GET或POST方法

案例:

  • views.py
# 视图层
from django.shortcuts import render, HttpResponse
from django.views import View
class CBVTest(View):

    def get(self, request):
        return render(request, 'cbv.html')

    def post(self, request):
        return HttpResponse('cbv post method')
  • url.py
# 路由层
from app import views
urlpatterns = [
    url(r'^cbv/', views.CBVTest.as_view()),  # 注意:这里的as_view方法是View类中的方法,且加括号运行了
]

CBV源码分析

流程:

自定义的视图类(CBVTest)继承了View,在路由里CBVTest.as_view()的这个as_view方法是View类的方法,并且及括号执行了,返回结果是在as_view方法中定义的view函数

此时路由就已经是CBVTest.view了,当请求过来触发view方法,在view方法中又触发了View类中的dispatch方法

dispatch方法中利用反射获取CBVTest中的GET或POST方法,做到请求分发的效果

源码:

View类中的as_view方法:

 @classonlymethod
    def as_view(cls, **initkwargs):
       ...

        # 定义了view方法
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.setup(request, *args, **kwargs)
            if not hasattr(self, 'request'):
                raise AttributeError(
                    "%s instance has no 'request' attribute. Did you override "
                    "setup() and forget to call super()?" % cls.__name__
                )
            # 调用View类中的dispatch方法,并将结果返回
            return self.dispatch(request, *args, **kwargs)
        view.view_class = cls
        view.view_initkwargs = initkwargs

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        
        # as_view方法将定义的view方法返回
        return view

View类中的dispatch方法:

    def dispatch(self, request, *args, **kwargs):
        # Try to dispatch to the right method; if a method doesn't exist,
        # defer to the error handler. Also defer to the error handler if the
        # request method isn't on the approved list.
        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)

drf请求源码分析

案例:

  • views.py
# 视图层
from rest_framework.views import APIView
from rest_framework.response import Response
user_list = [{'id': 1, 'name': 'Bob'}, {'id': 2, 'name': 'Tom'}]
class Users(APIView):
    def get(self, request, *args, **kwargs):
        return Response({
            'status': 0,
            'msg': 'ok',
            'results': user_list
        })
    def post(self, request, *args, **kwargs):
        # request对formdata,urlencoded,json三个格式参数均能解析
        name = request.data.get('name')
        id = len(user_list) + 1
        user = {'id': id, 'name': name}
        user_list.append(user)
        return Response({
            'status': '0',
            'msg': 'ok',
            'results': user
        })
  • urls.py
# 路由层
from app import views
urlpatterns = [
    url(r'^users/', views.Users.as_view()),
]

流程:

自定义的视图类(Users)继承了APIView,在路由里Users.as_view()的这个as_view方法是APIView类的方法,并且加括号执行了,在as_view方法里调用了APIView类的父类(View)里的as_view方法,并且禁用了csrf

APIView重写了dispatch方法,所以在as_view方法中调用的是APIView的dispatch方法(完成了很多重要工作),完成任务分发

补充:APIView中的dispatch方法中完成了二次封装request,认证,权限,频率功能

源码:

    def as_view(cls, **initkwargs):
        if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
            def force_evaluation():
                raise RuntimeError(
                    'Do not evaluate the `.queryset` attribute directly, '
                    'as the result will be cached and reused between requests. '
                    'Use `.all()` or call `.get_queryset()` instead.'
                )
            cls.queryset._fetch_all = force_evaluation

        view = super().as_view(**initkwargs)  # APIView调用其父类的as_view方法,返回的这个view就是Django里View类中as_view返回的那个view
        view.cls = cls  # 将视图类添加到view的名称空间中,后续要在view中实例化视图类,拿到视图类的对象
        view.initkwargs = initkwargs  # 实例化视图类时用到

        return csrf_exempt(view)  # 局部禁用csrf(只有直接或间接继承了APIView的视图类才禁用), DRF有自己的认证类,而且高级
    
    def dispatch(self, request, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs
        #  请求模块: initialize_request 对requset进行了二次封装
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?
        # 这个try捕获异常 (1.捕获三大认证不通过时抛出的异常, 2.捕获视图类请求方式没实现时抛出的异常 ......走到逻辑里发生的异常都会被捕获)
        try:
            # 三大认证模块
            self.initial(request, *args, **kwargs)
            # 三大认证通过后才能访问视图类
            # Get the appropriate handler method
            # 这里和原生Django一样的操作: 利用反射获取视图类中与前端请求方法一样的方法
            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)
            # 例如: response = post(self, request, *args, **kwargs)
            # response 就是返回给前台的结果

        except Exception as exc:  # exc是异常信息
            # DRF 异常模块,
            response = self.handle_exception(exc)  # response就是被DRF处理过的异常,会返回给前台
        # 这里将response进行了二次封装,finalize_response就是DRF的渲染模块,针对浏览器, 工具(Postman), Ajax 有不同的渲染方式
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response
    

initial(self, request, *args, **kwargs)
    # 认证
    self.perform_authentication(request)
    # 权限
    self.check_permissions(request)
    # 频率
    self.check_throttles(request)

drf中的request对象

源码入口

APIView类的dispatch方法中:request = self.initialize_request(request, *args, **kwargs)

源码分析

     def initialize_request(self, request, *args, **kwargs):
        # 准备解析的数据  parser_context就是准备解析的数据
        parser_context = self.get_parser_context(request)
        # 二次封装request,使DRF的request兼容Django的request
        return Request(
            request,  # 原生的request
            parsers=self.get_parsers(),  # 解析模块(在此处解析), 解析form_data, URLencoding, json格式的数据
            authenticators=self.get_authenticators(),  # get_authenticators就是一堆认证类的对象组成的列表
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
  • 1) drf 对原生request做了二次封装,request._request就是原生request
  • 2) 原生request对象的属性和方法都可以被drf的request对象直接访问(兼容)
  • 3) drf请求的所有url拼接参数均被解析到query_params中,所有数据包数据都被解析到data

基于restful规范的drf接口

api应用的子路由: api/url.py

from django.conf.urls import url
​
from . import views
urlpatterns = [
    # 这里的as_view是APIView类中的,作用是禁用了csrf_exempt,并返回view,调用了View类中的as_view方法
    url(r'^test/$',views.Test.as_view())
]

视图层: views.py

# APIView本质是继承了View
class Test(APIView):
    def get(self, request, *args, **kwargs):
        # url 拼接的参数
        print(request._request.GET)  # 二次封装request
        print(request.GET)  # 兼容
        print(request.query_params)  # 扩展,GET请求拼接的参数这里都有
        return Response('drf get ok')
    def post(self, request, *args, **kwargs):
        # 请求携带的数据包
        print(request._request.POST)  # 二次封装方式,没有json方式的数据
        print(request.POST)  # 兼容,没有json方式的数据
        print(request.data)  # 拓展,兼容性最强,三种数据方式都可以
​
        print(request.query_params)  # post拼接的数据也可以接受到
​
        return Response('drf post ok') 

url拼接参数 : 只有一种传参方式,参数都在query_params中

数据包参数 : 有三种传参方式 form-data,urlencoded,json,参数都在data中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值