drf-解析器组件(代码片段)

jiumo jiumo     2023-02-02     544

关键词:

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方法会被执行,至于执行什么逻辑,我们可以自定义。



drf之解析器组件及序列化组件(代码片段)

  通过上一篇博客的学习,我们已经有了一个共识,Django无法处理application/json协议请求的数据,即,如果用户通过application/json协议发送请求数据到达Django服务器,我们通过request.POST获取到的是一个空对象。  DjangoRestFramework... 查看详情

14.drf-解析器(代码片段)

Djangorestframework(5)----解析器解析器(1)api/urls.py#api/urls.pyfromdjango.urlsimportpath,re_pathfrom.viewsimportUserView,PaserViewurlpatterns=[re_path(‘(?P<version>[v1|v2]+)/users/‘,UserView.as_view( 查看详情

drf解析组件以及序列化组件(代码片段)

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

drf的解析器和渲染器(代码片段)

解析器解析器的作用解析器的作用就是服务端接收客户端传过来的数据,把数据解析成自己可以处理的数据。本质就是对请求体中的数据进行解析。在了解解析器之前,我们要先知道Accept以及ContentType请求头。Accept是告诉对方我... 查看详情

drf之序列化组件(代码片段)

1一序列化器-Serializer作用:1.序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串2.反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型3.反序列化,完成数据校验功能1.1定... 查看详情

drf序列化组件(代码片段)

目录1一序列化器-Serializer1.1定义序列化器1.2创建Serializer对象1.3序列化器的使用1.3.1序列化1.3.1.1基本使用1.3.1.2高级用法source和serializers.SerializerMethodField()的用法1.3.2反序列化1.3.2.1数据验证1)validate_字段名2)validate3)validators1.3.2.2反序... 查看详情

drf版本认证权限限制解析器和渲染器(代码片段)

...器3.1PageNumberPagination3.2LimitOffsetPagination3.3CursorPagination六.解析器和渲染器七.对DRF中的request对象的相关总结1.查看源码2.总结八.版本,认证,权限,限制,分页--源码查看方法九.补充知识1.issubset()2.语法糖setter,getter,deleter3.ORM之update_or_crea... 查看详情

drf版本认证权限限制解析器和渲染器(代码片段)

...器3.1PageNumberPagination3.2LimitOffsetPagination3.3CursorPagination六.解析器和渲染器七.对DRF中的request对象的相关总结1.查看源码2.总结八.版本,认证,权限,限制,分页--源码查看方法九.补充知识1.issubset()2.语法糖setter,getter,deleter3.ORM之update_or_crea... 查看详情

drf解析器和渲染器(代码片段)

一,DjangoRESTFramework解析器根据请求头content-type选择对应的解析器就请求体内容进行处理。a.仅处理请求头content-type为application/json的请求体fromdjango.conf.urlsimporturl,includefromweb.views.s5_parserimportTestViewurlpatterns=[url(r‘test/‘,TestView.as_vie... 查看详情

drf解析器(代码片段)

1.简介作用:将传过来的数据,解析成字典2.使用分为局部使用和全局使用局部使用,什么都不写,默认就是parser_classes=[JSONParser,FormParser] fromrest_framework.viewsimportAPIViewfromrest_framework.parsersimportJSONParser,FormParserclassTest(APIView):parse... 查看详情

drf分页器组件(代码片段)

目录一、使用drf分页器一、使用drf分页器drf的GenericAPIView这个视图基类提供了三种分页器:但是这里只列举两个,个人认为,最后一个没什么用PageNumberPagination类(常用)作用:一页显示的条数#url:示例http://http://127.0.0.1:8000/books/?... 查看详情

drf框架(代码片段)

...BV比FBV的优势三、drf框架的基础试图类APIView:请求模块、解析模块、渲染模块、响应模块、异常模块四、drf核心组件序列化组件:将后台的任何数据,进行序列化返回给前台;将前台的数据反序列化成后台model对象再入库三大认... 查看详情

04drf视图组件(代码片段)

一.视图DjangoRESTframwork提供的视图的主要作用:控制序列化器的执行(检验、保存、转换数据)控制数据库查询的执行1.1视图继承关系1.2视图2个视图基类APIViewGenericAPIView[通用视图类]1.2.1APIViewrest_framework.views.APIViewAPIView是RESTframewor... 查看详情

drf序列化组件(代码片段)

1序列化组件介绍1.序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串2.反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型3.反序列化,完成数据校验功能2简单使用1写一... 查看详情

drf部分源码简介及序列化组件(代码片段)

一:解析模块(1)作用:  (1)drf给我们通过了多种解析数据包方式的解析类  (2)我们可以通过配置来控制前台提交的哪些格式的数据后台在解析,哪些数据不解析  (3)全局配置就是针对每一个视图类,局部配置... 查看详情

django-rest-framework---总结(代码片段)

...染模块:浏览器和Postman请求结果渲染数据的方式不一样3.解析模块4.异常模块5.响应模块序列化组件:1.Serializer2.序列化与反序列化整合(重点)3.整体单改4.单与整体局部修改视图类视图类传递参数给序列化类二次封装Response类视... 查看详情

二:drf视图(代码片段)

...展了HttpRequest类的Request类的对象。2RESTframework提供了Parser解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON、表单等)将请求数据进行parse解析,解析为类字典[QueryDict]对象保存到Request对象中。3Request对象... 查看详情

drf请求与响应(代码片段)

...展了HttpRequest类的Request类的对象。RESTframework提供了Parser解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON、表单等)将请求数据进行parse解析, 查看详情