DRF学习之APIView执行流程以及request对象源码分析

一、基于View编写5个接口

1、创建模型表

  • models.py
from django.db import models


class Book(models.Model):
    name = models.CharField(max_length=64)
    price = models.IntegerField()
    publish = models.CharField(max_length=32)

2、视图函数

  • views.py
from django.shortcuts import render
from django.http import JsonResponse

# 1 继承View 5 个接口
from django.views import View
from .models import Book


# 127.0.0.1:8000/app01/books/
class BookView(View):
    def get(self, request):
        obj_list = Book.objects.all()
        # 序列化
        l = []
        for obj in obj_list:
            l.append({'name': obj.name, 'price': obj.price, 'publish': obj.publish})
        return JsonResponse({'code': 100, 'msg': '查询成功', 'results': l})

    def post(self, request):
        # 取出前端传入的数据:
        # 1 post:只有urlencoded和form-data格式从request.POST中才能取出
        # 2 如果是json格式:request.POST 是空的
        data = request.POST
        book = Book.objects.create(name=data.get('name'), price=data.get('price'), publish=data.get('publish'))
        return JsonResponse({'code': 100, 'msg': '新增成功'})

from urllib.parse import unquote
# 127.0.0.1:8000/app01/books/1
class BookDetailView(View):
    def get(self, request, pk):
        obj = Book.objects.filter(pk=pk).first()
        return JsonResponse(
            {'code': 100, 'msg': '查询成功', 'result': {'name': obj.name, 'price': obj.price, 'publish': obj.publish}})

    def delete(self, request, pk):
        Book.objects.filter(pk=pk).delete()
        return JsonResponse({'code': 100, 'msg': '删除成功'})


    def put(self, request, pk):
        '''
        1 前端使用put传入的数据,在request.POST取不出来---》django没有帮咱们处理
        2 咱们需要自己从body中取出---》处理
        3 在传输过程中,字符串做了 url的编码 --》自行转码
        '''
        print(**request.POST)
        # 1 put 请求提交数据---》body提中--》从request.POST取不出值的
        # 2 从body中取出,自己处理--》urlencode编码 name=xx&price=99&publish=sdf
        # 3 request.body bytes格式转成字符串
        body_str = request.body.decode('utf-8')
        # 4 使用 & 切分
        body_list = body_str.split('&')  # [name=xx,price=99,publish=sdf]
        # 5 循环,使用 = 切分, 放到字典中
        dic = {}
        for item in body_list:
            # 转码只给value转
            dic[item.split('=')[0]] = unquote(item.split('=')[1])
        print(dic)
        # 6 打散了,修改数据
        Book.objects.filter(pk=pk).update(**dic)
        return JsonResponse({'code': 100, 'msg': '修改成功'})

3、路由

  • urls.py
from django.urls import path,include
urlpatterns = [
    path('app01/', include('app01.urls')),
]
  • app01/urls.py
from django.urls import path
from .views import BookView, BookDetailView

urlpatterns = [
    path('books/', BookView.as_view()),
    path('books/<int:pk>', BookDetailView.as_view()),
]

二、基于APIView编写5个接口

1、问题

(1)问题一

  • 上面基于View写的五个接口在前端body体中的数据,有时候能用request.POST取出来,但有时候却取不出来。

  • 而基于APIView写的接口可以通过request.data,无论什么编码格式,无论什么请求方式都能得到字典格式的数据。

(2)问题二

  • csrf要求发送post,put或delete请求的时候,是先以get方式发送请求,服务端响应时会分配一个随机字符串给客户端,客户端第二次发送post,put或delete请求时携带上次分配的随机字符串到服务端进行校验。
  • 而基于APIView会自动注释掉csrf中间件

(3)问题三

  • 在新增数据的时候,有个需要我们知道的东西

  • 首先APIView会帮咱们看POST中有没有数据,如果有,直接赋值给data,如果没有,他去body中转成字典再给data

    • json格式:
      • POST没有东西 QueryDict
      • data有数据 dict
    • urlencoded 或 form-data
      • POST有东西 QueryDict
      • data有数据 QueryDic
  • 反正用request.data就对了

2、视图函数

  • views.py
from rest_framework.views import APIView


class BookView(APIView):
    def get(self, request):
        obj_list = Book.objects.all()
        l = []
        for obj in obj_list:
            l.append({'name': obj.name, 'price': obj.price, 'publish': obj.publish})
        return JsonResponse({'code': 100, 'msg': '查询成功', 'results': l})

    def post(self, request):
        # 前端无论以什么编码格式--》提交到后端你的数据,都会被解析到 request.data 中,是个字典
        # 如果是urlencoded或form-data编码--》不能直接这样写,要写成如下
        Book.objects.create(name=request.data.get('name'),price=request.data.get('price'),publish=request.data.get('publish'))
        # 如果是json格式编码,下面没问题
        # Book.objects.create(**request.data)


        print(request.POST)
        print(request.data)
        # 本质原因是?
        ''' APIView帮咱们呢---》看POST中有没有数据,如果有,直接赋值给data,如果没有,他去body中转成字典再给data
         json格式:
            POST没有东西 QueryDict
            data有数据  dict
        urlencoded或form-data
            POST有东西 QueryDict
            data有数据  QueryDict
        '''
        return JsonResponse({'code': 100, 'msg': '新增成功'})


# 127.0.0.1:8000/app01/books/1
class BookDetailView(APIView):
    def get(self, request, pk):
        obj = Book.objects.filter(pk=pk).first()
        # 加个逻辑判断
        return JsonResponse(
            {'code': 100, 'msg': '查询成功', 'result': {'name': obj.name, 'price': obj.price, 'publish': obj.publish}})

    def delete(self, request, pk):
        Book.objects.filter(pk=pk).delete()
        return JsonResponse({'code': 100, 'msg': '删除成功'})

    def put(self, request, pk):
        # 跟post新增数据一样,编码格式不是json就会报错
        Book.objects.filter(pk=pk).update(**request.data)
        return JsonResponse({'code': 100, 'msg': '修改成功'})

3、总结

  • 只要继承了APIView,就不需要处理csrf
  • 以后前端提交的数据,无论什么编码,无论什么请求方式,数据都从 request.data 取,它是字典
    • 可能是:QueryDict
      • 不是真正的字典,用起来,可能会有点小问题
    • 可能是:dict

三、APIView执行流程分析

1、简单描述APIView执行流程

路由: path('Book/',view.BookView.as_view()) --> BookView.as_view()(request) --> BookView中没有as_view,找父类APIView的as_view---> 得到APIView的as_view的执行结果(本质还是用了View类的as_view内的view闭包函数,去掉了csrf的认证)--> 当请求来了-->触发View类as_view内的view的闭包函数执行,并且request传入 --> 调用了self.dispatch --> self是视图类的对象( BookView的对象),从BookView类中的找dispatch,但是找不到 --> 再去父类APIView中找,APIView有 --> 本质就是执行APIView类中的dispatch                                                            `

ps:点击设置,找到Tree Appearance,再把Show Memebers勾上,这样我们在想要找到某个函数的时候就可以点击那个瞄准框一样的图标直接定位了

在这里插入图片描述

2、源码详细介绍

  • 继承APIView的类,通过路由的BookView.as_view()方法可以看到APIView继承了Django的View类

在这里插入图片描述

  • APIView的as_view
@classmethod # 绑定给类的方法,类来调用
def as_view(cls, **initkwargs):
    # super代指父类--> 父类是View-->下面的图可以看见--> self.dispatch()
    # 这个view 还是原来View的as_view的执行结果--》as_view中有个view内部函数
    view = super().as_view(**initkwargs)
    # 只要继承了APIView,不需要处理csrf
    '''
    @csrf_exempt
    def index(request):
    	pass
    等同于  index=csrf_exempt(index)
    以后调用index,其实调用的 是csrf_exempt(index)()
    '''
    return csrf_exempt(view)

在这里插入图片描述

在这里插入图片描述

  • 请求(request)来了,真正执行的是:csrf_exempt(view)(request)—> 去除了csrf的view(request)—> self.dispatch()
  • 真正执行的是self.dispatch(request)—> self 是 视图类的对象(BookView的对象),但是在视图类里面找不到dispatch—> 找父类APIView中的dispatch方法
  • 现在要看 APIView的dispatch
def dispatch(self, request, *args, **kwargs):
    # 1 包装了新的request对象---》现在这个requets对象,已经不是原来django的request对象了
    request = self.initialize_request(request, *args, **kwargs)
    try:
        # 2 APIView的initial--》三件事:三大认证:认证,频率,权限
        self.initial(request, *args, **kwargs)
        # 3 就是执行跟请求方式同名的方法
        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)
                # 4 如果在三大认证或视图类的方法中出了异常,会被统一捕获处理
    except Exception as exc:
        response = self.handle_exception(exc)
    self.response = self.finalize_response(request, response, *args, **kwargs)
    return self.response

在这里插入图片描述

3、执行流程总结

  • 只要继承了APIView,就没有csrf限制了
  • 只要继承了APIView,request就是新的request了,它有data属性
  • 在执行跟请求方式同名的方法之前,执行了三大认证:认证,频率,权限
  • 只要在三大认证或者视图类的方法中出了异常,都会被捕获,统一处理

四、request对象源码分析

1、简单描述新request对象的生成流程

执行APIView中的dispatch方法时---> self.initialize_request(request)--->self是视图类的对象,但是在其中没有找到initialize_request方法,在父类APIView中有,于是执行---> 返回给了Request类---> self._request = request
APIView中的dispatch中---> self.request=request---> 后续所有视图类中都可以通过self拿到request对象(新的request)

在这里插入图片描述

在这里插入图片描述

2、新老request对象的区别

(1)不同类的对象

  • 新的Request具体是哪个类的对象

    • rest_framework.request.Request 类的对象
  • 老的request是哪个类的对象

    • django.core.handlers.wsgi.WSGIReques类t的对象

(2)支持的操作不同

  • 老的request可以

    • request.method
    • request.path
    • request.META.get(‘REMOTE_ADDR’)
    • request.FILES.get()
    • ​ …
  • 新的request可以

    • 新的request支持之前所有老request的操作
    • request.data–> 请求体的数据–> 方法包装成了数据属性
    @property
    def data(self):
        if not _hasattr(self, '_full_data'):
            self._load_data_and_files()
            return self._full_data
    
    • request.query_params就是原来的老request.GET,这样是为了贴合restful规范
    @property
    def query_params(self):
        return self._request.GET
    
    • request._request 就是老的request

3、源码分析

为什么:之前request对象如何用,现在新的request对象还是如何用?没有继承关系吗?

  • 在视图类中使用的request对象是新的request对象,老的是request._request,新的request.GET拿到的还是老的GET,原理如下(Request类重写了__getattr__)

  • from rest_framework.request import Request
    __getattr__: .拦截方法,对象.属性,如果属性不存在,就会触发__getattr__执行
    requst.method -->新的request没有--> 会触发新的Request类中的 __getattr__ ---> 得到老的request对象中的方法
    
  • 何为魔法方法?不需要主动调用,某种状态下会触发。

在这里插入图片描述

4、总结

  • 之前如何用,还是如何用
  • request.data得到请求体的数据,data方法包装成了数据属性
  • request.query_params就是原来的request.GET,是为了贴合restful规范
  • request._request 就是老的request
  • 魔法方法之 __getattr__

属性不存在,就会触发__getattr__执行
requst.method -->新的request没有–> 会触发新的Request类中的 getattr —> 得到老的request对象中的方法


- 何为魔法方法?不需要主动调用,某种状态下会触发。

[外链图片转存中...(img-kqqmNyTW-1713702513986)]



## 4、总结

- 之前如何用,还是如何用
- request.data得到请求体的数据,data方法包装成了数据属性
- request.query_params就是原来的request.GET,是为了贴合restful规范
- request._request 就是老的request
- 魔法方法之` __getattr`__







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值