DRF - 解析器组件

DRF之解析器组件

引入

Django RestFramework帮助我们实现了处理application/json协议请求的数据,另外,我们也提到,如果不使用DRF,直接从request.body里面拿到原始的客户端请求的字节数据,经过decode,然后json反序列化之后,也可以得到一个Python字典类型的数据。

但是,这种方式并不被推荐,因为已经有了非常优秀的第三方工具,那就是Django RestFramework的解析器组件。

解析器组件的使用

首先,来看看解析器组件的使用,稍后我们一起剖析其源码:

from django.http import JsonResponse

from rest_framework.views import APIView
from rest_framework.parsers import JSONParser, FormParser
# Create your views here.

class LoginView(APIView):
    parser_classes = [FormParser]

    def get(self, request):
        return render(request, ‘parserver/login.html‘)

    def post(self, request):
        # request是被drf封装的新对象,基于django的request
        # request.data是一个property,用于对数据进行校验
        # request.data最后会找到self.parser_classes中的解析器
        # 来实现对数据进行解析

        print(request.data)  # {‘username‘: ‘jiumo‘, ‘password‘: 123}

        return JsonResponse({"status_code": 200, "code": "OK"})

使用方式非常简单,分为如下两步:

  • from rest_framework.views import APIView
  • 继承APIView
  • 直接使用request.data就可以获取Json数据

如果我们只需要解析Json数据,不允许任何其他类型的数据请求,可以这样做:

  • from rest_framework.parsers import JsonParser
  • 给视图类定义一个parser_classes变量,值为列表类型[JsonParser]
  • 如果parser_classes = [], 那就不处理任何数据类型的请求了

首先,需要明确一点,我们肯定需要在request对象上做文章,只有有了用户请求,我们的解析才有意义,没有请求,就没有解析,更没有处理请求的逻辑,所以,我们需要弄明白,在整个流程中,request对象是什么时候才出现的,是在绑定url和处理视图之间的映射关系的时候吗?我们来看看源码:

@classonlymethod
def as_view(cls, **initkwargs):
    """Main entry point for a request-response process."""
    for key in initkwargs:
        if key in cls.http_method_names:
            raise TypeError("You tried to pass in the %s method name as a "
                            "keyword argument to %s(). Don‘t do that."
                            % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))

def view(request, *args, **kwargs):
    self = cls(**initkwargs)
    if hasattr(self, ‘get‘) and not hasattr(self, ‘head‘):
        self.head = self.get
        self.request = request
        self.args = args
        self.kwargs = kwargs
        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=())
    return view

看到了吗?在执行view函数的时候,那么什么时候执行view函数呢?当然是请求到来,根据url查找映射表,找到视图函数,然后执行view函数并传入request对象,所以,如果是我,我可以在这个视图函数里面加入处理application/json的功能:

@classonlymethod
def as_view(cls, **initkwargs):
    """Main entry point for a request-response process."""
    for key in initkwargs:
        if key in cls.http_method_names:
            raise TypeError("You tried to pass in the %s method name as a "
                            "keyword argument to %s(). Don‘t do that."
                            % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))

def view(request, *args, **kwargs):
    if request.content_type == "application/json":
        import json
        return HttpResponse(json.dumps({"error": "Unsupport content type!"}))

    self = cls(**initkwargs)
    if hasattr(self, ‘get‘) and not hasattr(self, ‘head‘):
        self.head = self.get
        self.request = request
        self.args = args
        self.kwargs = kwargs
        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=())
    return view

看到了吧,然后我们试试发送json请求,看看返回结果如何?事实上,我们可以在这里,也可以在这之后的任何地方进行功能的添加。

那么,DRF是如何做的呢?我们在使用的时候只是继承了APIView,然后直接使用request.data,所以,斗胆猜测,功能肯定是在APIView中定义的,具体在哪个地方呢?接下来,我们一起来分析一下DRF解析器源码,看看DRF在什么地方加入了这个功能。

我们通过面向对象的方式,给类的某个方法新增了功能,调用重写的方法,就实现了功能扩展,但是上面除了request.data,我们没有调用任何新的方法,所以,问题就在这个request.data上,它绝不仅仅是一个普通的对象属性。

好了,有了这个共同的认识,我们接下来验证一下我们的看法。

解析器组件源码剖析

请看下图:

上图详细描述了整个过程,最重要的就是重新定义的request对象,和parser_classes变量,也就是我们在上面使用的类变量。好了,通过分析源码,验证了我们的猜测。

源码部分:

当我们发送post请求时,request.data会在这一步进行解析数据。 所以关键的地方在.data部分。data是request的静态方法。所以我们找源码的时候应该找request中的静态方法data。我们都知道这个request是在APIView中的dispatch方法重新封装了。所以应该去APIVew中找dispatch方法,需要找到request怎么被重新封装的。

点进源码:

点进Resquest源码,找data静态方法:

这一步就是数据解析的过程,点进去源码,

点进源码:

点进源码self.parser,

这就又回到实例化request的时候了。

点进去源码:

这里这个self,一层一层往上找,就找到了这个self.指的是视图类函数。所以在试图类函数中,定义了哪些解析器,他就支持哪些解析器。

class LoginView(APIView):
    parser_classes = [FormParser]

    def get(self, request):
        pass

DRF支持三种格式编码的解析:

? from rest_framework.parsers import JSONParser,FormParser,MultiPartParser

点进去源码我们可以看到DRF中的解析器,默认支持三种:

? JSONParser, FormParser, MultiPartParser

那如果在我们的试图类函数中没有定义这个变量,那他是怎么走默认的解析器?

那我们点进parser_classes源码:

当我们点进api_settings中发现,api_settings是一个类的实例化对象,

但是在这个类中并没有DEFAULT_PARSER_CLASSES方法。

这里看一个知识点,在python基础中,一个类实例化后会进行初始化,执行__init__方法,但对于实例化没有的属性会走__getattr__方法。

所以,没有DEFAULT_PARSER_CLASSES方法,会走__getattr__方法,

而在我们的DEFAULTS中有 DEFAULT_PARSER_CLASSES

接下来:

点进去settings,找REST_FRAMEWORK,找不到就去全局settings中找。找不到就去默认字典字典中找,如果还是找不到,就赋值为空字典。

这就是解析器的源码

知识点复习回顾:getattr

在学习面向对象时,我们知道可以通过对象加点号获取该对象的属性,也可以通过对象的dict访问属性,请看下面的代码:

class Father(object):
    country = "china"

class Person(Father):
    def __init__(self, name, age):
        self.name = name
        self.age = age

p = Person("pizza", 18)
print(p.__dict__)       # {‘name‘: ‘pizza‘, ‘age‘: 18}
print(Person.__dict__)  # {‘__module__‘: ‘__main__‘, ‘__init__‘: <function Person.__init__ at 0x103f132f0>, ‘__doc__‘: None}
print(p.name)           # jiumo
print(p.age)            # 18
print(p.country)        # china 如果对象不存在这个属性,则会到其父类中查找这个属性
print(p.hobby)          # 如果在父类中也找不到这个属性,则会报错:AttributeError: ‘Person‘ object has no attribute ‘hobby‘

对象的属性查找首先会在该对象的一个名为dict的字典中查找这个属性,如果找不到,则会到其父类中查找这个属性,如果在父类中都也找不到对应的属性,这会抛出异常AttributeError,我们可以通过在类中定义一个getattr来重定向未查找到属性后的行为,请看下面的代码:

class Father(object):
    country = "china"

class Person(Father):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __getattr__(self, value):
        raise ValueError("属性%s不存在" % value)

p = Person("pizza", 18)
print(p.hobby)  # ValueError: 属性hobby不存在

可以看到,我们能够重新定义异常,也可以做其他任何事情,这就是getattr,一句话总结,通过对象查找属性,如果找不到属性,且该对象有getattr方法,那么getattr方法会被执行,至于执行什么逻辑,我们可以自定义。

原文地址:https://www.cnblogs.com/jiumo/p/10105431.html

时间: 2024-08-30 02:25:13

DRF - 解析器组件的相关文章

django rest framework 解析器组件 接口设计,视图组件 (1)

一.解析器组件 -解析器组件是用来解析用户请求数据的(application/json), content-type 将客户端发来的json数据进行解析 -必须适应APIView -request.data触发 二.序列化组件 2.1.django 自带组件serializer 2.1.1 from django.serializers import serialize2.1.2 origin_data = Book.objects.all()2.1.3 serialized_data = se

DjangoRestFramework学习一之restful规范、APIview、解析器组件、Postman等

DjangoRestFramework学习一之restful规范.APIview.解析器组件.Postman等 本节目录 [TOC] 一 预备知识 预备知识:django的CBV和FBV CBV(class based view):多用,简单回顾一下 FBV(function based view): CBV模式的简单操作:来个登陆页面吧 login.html文件内容如下: <!DOCTYPE html> <html lang="en"> <head>

18.DjangoRestFramework学习一之restful规范、APIview、解析器组件、Postman等

一 预备知识 预备知识:django的CBV和FBV CBV(class based view):多用,简单回顾一下 FBV(function based view): CBV模式的简单操作:来个登陆页面吧 login.html文件内容如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</tit

【DRF解析器和渲染器】 &#423984;

目录 解析器 Django中的解析器 DRF中的解析器 DRF中的渲染器 原文: http://blog.gqylpy.com/gqy/294 "@ *** 解析器 解析器的作用就是服务端接收客户端传过来的数据,把数据解析成自己想要的数据类型的过程. 本质就是对请求体中的数据进行解析. Content-type:用于声明我给你传的是什么类型的数据 : application/x-ww-form-urlencoded 表单数据类型 : multipart/form-data 文件 : applic

DRF解析组件以及序列化组件

一.知识点回顾: 1.三元运算: 三元运算能够简化我们的代码,请看如下代码: # 定义两个变量 a = 1 b = 2 # 判断a的真假值,如果为True,则将判断表达式的前面的值赋给c,否则将判断表达式后面的值赋给c c = a if a else b print(c) # 1 # 因为a的真假值判断为True,所以c为1 # 定义两个变量 a = 0 b = 2 # 判断a的真假值,如果为True,则将判断表达式的前面的值赋给c,否则将判断表达式后面的值赋给c c = a if a else

DRF-解析器组件源码解析

解析器组件源码解析 解析器组件源码解析 1 执行request.data 开始找重装的request中的data方法 2 在dispatch找到重装的request def dispatch(self, request, *args, **kwargs): request = self.initialize_request(request, *args, **kwargs) ***这里开始找data 3 在initialize_request中找到实例request的类Request() def

MySQL技术探索01实现SQL语法解析器

本文将介绍如何使用开源的语法和词法分析框架bison和flex来实现SQL解析器.出于技术学习的目的,本文做描述的微型SQL解析器仅能实现对微型SQL的语法解析. 1.MySQL中的SQL解析器 包括JDBC.ODBC.ADO等等关系数据库客户端应用开发框架在内的各种SDK,核心功能是帮助程序员简化各种客户端的数据库操作,同时将SQL语句通过网络形式发送给MySQL等关系数据库的服务器进程.MySQL服务器进行负责解析并执行这些SQL语句.SQL语句中的语法规则多种多样,MySQL服务器是如何实

DRF 版本、认证、权限、限制、解析器和渲染器

目录 一.DRF之版本控制 为什么要有版本控制? DRF提供的版本控制方案 版本的使用 全局配置 局部配置(使用较少) 二.DRF之认证 内置的认证 步骤 三.DRF之权限 1.自定义一个权限类 2.权限 局部配置 3.权限 全局配置 四.DRF之限制 1.使用自定义限制类 1.1自定义一个限制类 1.2限制 局部配置 1.3限制 全局配置 2.使用内置限制类 2.1定义内置限制类 2.2全局配置 五.DRF之分页 1.为什么要使用分页 2.DRF使用分页器 2.1分页模式 2.2全局配置 2.

Django框架深入了解_04(DRF之url控制、解析器、响应器、版本控制、分页)

一.url控制 基本路由写法:最常用 from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^books/', views.Books.as_view()), url(r'^book/', views.Book.as_view()), url(r'^login/