首页 > 编程语言 >2、APIView执行流程以及request对象源码分析

2、APIView执行流程以及request对象源码分析

时间:2024-04-14 21:55:32浏览次数:16  
标签:-- APIView self request 源码 data view

一、基于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勾上,这样我们在想要找到某个函数的时候就可以点击那个瞄准框一样的图标直接定位了

image

2、源码详细介绍

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

image

  • 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)

image

image

  • 请求(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

image

3、执行流程总结

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

四、request对象源码分析

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

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

image

image

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对象中的方法
    
  • 何为魔法方法?不需要主动调用,某种状态下会触发。

image

4、总结

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

标签:--,APIView,self,request,源码,data,view
From: https://www.cnblogs.com/xiao01/p/18134733

相关文章

  • JDK 源码阅读:java.lang.Object 类
    记录下自己阅读过程的笔记,如有错误,欢迎指正!源码参考:https://github.com/kangjianwei/LearningJDK/tree/master1.基本介绍在Java中,Object类是类层次结构的根类几乎每个Java类都直接或间接继承自Object类,意味着每个类都继承了Object的方法类结构:2.源码分析2.......
  • ES7.17.20连接时报错:java.lang.NoSuchMethodError: org.elasticsearch.client.Request
    1.报错详情:java.lang.NoSuchMethodError:org.elasticsearch.client.RequestOptions$Builder.removeHeader(Ljava/lang/String;)Lorg/elasticsearch/client/RequestOptions$Builder; atco.elastic.clients.transport.rest_client.RestClientOptions.addBuiltinHeaders(RestCli......
  • GRPC - Validating requests
      ......
  • GRPC - Distributing requests with load balancing
         ......
  • 短视频app源码,一文带你轻松搞懂前端大文件上传思路
    短视频app源码,一文带你轻松搞懂前端大文件上传思路文件上传是我们在平时开发短视频app源码中经常会遇到的业务,如果只是简单的文件上传那还不足以作为项目亮点,而当我们给它加上切片、续传的功能,就不一样了。本文会带大家搞明白这些功能的实现思路,主要聚焦于前端部分,基于Vue3......
  • 视频直播源码,不同业务场景需选择不同方案去缓存数据
    视频直播源码,不同业务场景需选择不同方案去缓存数据在开发视频直播源码时,针对不同业务场景,我们应该选择不同的方案去缓存数据。本文就针对最常见的存储方案和场景做一些分类和介绍一些在Vue/React中的高阶用法,助力前端开发体验和应用的稳定性。前端缓存方案确定不同场......
  • 在线直播系统源码,前后端大文件上传代码分析
    在线直播系统源码,前后端大文件上传代码分析前端代码:<template><div><[email protected]="hanldeClick"class="upload_container"><inputname="请上传文件"type="file"ref="uploadRef"......
  • Linux0.12内核源码解读(2)-Bootsect.S
    大家好,我是呼噜噜,在上一篇文章聊聊x86计算机启动发生的事?我们了解了x86计算机启动过程,MBR、0x7c00是什么?其中当bios引导结束后,操作系统接过计算机的控制权后,发生了哪些事?本文将揭开迷雾的序章-Bootsect.S回顾计算机启动过程我们先来回顾一下,上古时期计算机按下电源键的启动过程,......
  • 即时通讯技术文集(第36期):《跟着源码学IM》系列专题 [共12篇]
    为了更好地分类阅读52im.net总计1000多篇精编文章,我将在每周三推送新的一期技术文集,本次是第36 期。[-1-] 跟着源码学IM(一):手把手教你用Netty实现心跳机制、断线重连机制[链接] http://www.52im.net/thread-2663-1-1.html[摘要] 说到用Netty来开发IM或推送系统,以一个......
  • lodash已死?radash最全使用介绍(附源码说明)—— Array方法篇(3)
    前言我们已经介绍了radash的相关信息和部分Array相关方法,详情可前往主页查看;本篇我们继续介绍radash中Array的相关方法;下期我们将介绍解析radash中剩余的Array相关方法,并整理出Array方法使用目录,包括文章说明和脑图说明。Radash的Array相关方法详解iterate:把一个函数迭代......