码迷,mamicode.com
首页 > 其他好文 > 详细

RestFramework之解析器源码

时间:2018-09-18 19:20:33      阅读:428      评论:0      收藏:0      [点我收藏+]

标签:lse   实例   父类   query   tor   没有   请求方式   raw   相对   

一,restframework中的解析器

  • 在restframework中,默认可以解析JSONParser, FormParser, MultiPartParser(json数据, urlencode数据, 二进制文件类型数据(图片,视频等))这三种数据格式;
  • RestFramework中,当我们使用CBV写视图函数,继承RestFramework中的APIView时,只要我们在自己写的CBV类中,定义了  parser_classes = [ ]这个属性,那么restframework就会根据我们写在这      个属性中的值,而解析相对应的数据格式;
  • 这个parser_classes = [ ] 属性可以选择的值有三个  JSONParser, FormParser, MultiPartParser,即restframework给我们提供的三种可以解析的数据格式;

二,源码解析

首先,写一个django_demo:

views.py:

from django.http import JsonResponse
from app01 import models
from rest_framework.views import APIView
from rest_framework.parsers import JSONParser, FormParser, MultiPartParser

class Book(APIView):
    parser_classes = [FormParser]

    def get(self, request):
        book_list = models.Book.objects.all()
        temp = []
        for book in book_list:
            temp.append({"title": book.title, "publish_date": book.publishDate})
        return JsonResponse(temp, safe=False)

    def post(self, request):
        print("post")
        print(request.data)
        return HttpResponse("ok")

当我们使用postman发送post请求,数据时json格式是,会抛出:"Unsupported media type \"application/json\" in request."这样的错误,显然是不支持解析json数据格式的数据,但是我们的post方法中打印的post却打印了,说明问题出在了request.data上面; 也就是说在这里进行了格式解析验证。

看看执行流程:

首先,我们继承APIView类时,

url(r"^book", views.Book.as_view())

这里的as._view(),是APIView中的方法,(APIView继承的django的View类):

@classmethod
    def as_view(cls, **initkwargs):
      ‘‘‘ ... ‘‘‘
        view = super(APIView, cls).as_view(**initkwargs)  # 调用的原生的View中的as_view()方法
        view.cls = cls
        view.initkwargs = initkwargs

        # Note: session based authentication is explicitly CSRF validated,
        # all other authentication is CSRF exempt.
        return csrf_exempt(view)   # 这里return的时原生的View中的返回值

一旦有用户发送请求时,view函数就会执行:

        def view(request, *args, **kwargs):
            """ ... """
       self = cls(**initkwargs)  # 这里的self就是我们自己写的那个CBV类的实例化的self
            return self.dispatch(request, *args, **kwargs)

执行self.dispatch()方法,先从我们的CBV类中找,接着在APIView中找,发现APIView中有dispatch()方法:

    def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django‘s regular dispatch,
        but with extra hooks for startup, finalize, and exception handling.
        """
        self.args = args
        self.kwargs = kwargs
        # 重新封装了django的request对象
        request = self.initialize_request(request, *args, **kwargs)
         # 把新的request对象重新赋给了原来的request
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
        # 按照请求方式分发到我们写的不同的函数中
            self.initial(request, *args, **kwargs)

            # Get the appropriate handler method
            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)

        except Exception as exc:
            response = self.handle_exception(exc)

        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response  # 重新封装的响应对象

重点来了,解析器是怎么解析的?我们去看一下这句话   request = self.initialize_request(request, *args, **kwargs)

 在这个方法中:self.initialize_request():

    def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)

        return Request(    # restframework自己的Request类实例化得到的新的 request对象
            request,  # 这个request就是传进来的那个旧的request对象
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )

应为之前我们就是卡在了request.data那里, 接下来去看看 Request类中的data方法:

@property   # 是一个静态方法
    def data(self):
        if not _hasattr(self, _full_data):
            self._load_data_and_files()
        return self._full_data

再看   self._load_data_and_files():

    def _load_data_and_files(self):
        """
        Parses the request content into `self.data`.
        """
        if not _hasattr(self, _data):
            self._data, self._files = self._parse()   # 关心这句话
            if self._files:
                self._full_data = self._data.copy()
                self._full_data.update(self._files)
            else:
                self._full_data = self._data

看看  self._data, self._files = self._parse():

 def _parse(self):
        """
        Parse the request content, returning a two-tuple of (data, files)

        May raise an `UnsupportedMediaType`, or `ParseError` exception.
        """

     """ ...... """
parser = self.negotiator.select_parser(self, self.parsers) # 这里就是我们想看的 具体是怎么选择解析器的 if not parser: raise exceptions.UnsupportedMediaType(media_type) try: parsed = parser.parse(stream, media_type, self.parser_context) #这里就是实际在解析数据了,这个不用关心(无非就是针对不同的数据格式,使用相对应的格式去解析数据,封装到data中) except Exception: # If we get an exception during parsing, fill in empty data and # re-raise. Ensures we don‘t simply repeat the error when # attempting to render the browsable renderer response, or when # logging the request or similar. self._data = QueryDict(‘‘, encoding=self._request._encoding) self._files = MultiValueDict() self._full_data = self._data raise # Parser classes may return the raw data, or a # DataAndFiles object. Unpack the result as required. try: return (parsed.data, parsed.files) except AttributeError: empty_files = MultiValueDict() return (parsed, empty_files)

再看是如何选择解析器的: self.negotiator.select_parser(self, self.parsers):  

注意这个方法中传递两个参数,self ,就是我们那个新的request对象(在Request()那里得到的),第二个参数就是self.parsers,即实例属性,也是在Request()实例化的时候传递的参数

    def select_parser(self, request, parsers):
        """
        Given a list of parsers and a media type, return the appropriate
        parser to handle the incoming request.
        """
        for parser in parsers:  # 实际上是在循环我们传进来的那个self.parsers,
            if media_type_matches(parser.media_type, request.content_type):
                return parser
        return None

可以看到该方法中,是在循环那个self.parsers,也就是说,restframework可以解析那种数据格式,取决那个新的request对象实例化的时候(Request())中那个参数self.parsers:

这样我们就需要去看看 Request() 实例化的时候到底是怎么传递参数的:

    def initialize_request(self, request, *args, **kwargs):  # 这里的self是我们的CBV类的实例化对象,一步步传递过来的
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)

        return Request(
            request,
            parsers=self.get_parsers(), 
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )

在 self.get_parsers()中:

def get_parsers(self):
        """
        Instantiates and returns the list of parsers that this view can use.
        """
        return [parser() for parser in self.parser_classes]  # 看到的就是我们定义的  parser_classes这个属性, 优先在我们的CBV类中查找

 那么如果 我们有自己定义 parser_classes, 就会去循环我们写的那个属性的列表,也就实现了我们自己自定义可以解析的的数据格式;(前提是我们写的解析器是restframework中能解析的)。

 但是!但是!但是! 如果我们没有自己定义 parser_classes, 就会去父类中找,也就是APIView中,在APIView中:

parser_classes = api_settings.DEFAULT_PARSER_CLASSES  # 默认的parser_classes

直接使用api_settings . 这个DEFAULT_PARSER_CLASSES, 我们去看看api_settings:

api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)  #是APISettings的实例化对象

而我们发现在 APISettings这个类中就没有  DEFAULT_PARSER_CLASSES这个属性,也就是说用 api_settings.DEFAULT_PARSER_CLASSES 拿不到属性,那么就会执行 __getattr__()方法:

    def __getattr__(self, attr):  # 这里的attr就是 DEFAULT_PARSER_CLASSES 
        if attr not in self.defaults:  
            raise AttributeError("Invalid API setting: ‘%s‘" % attr)

        try:
            # Check if present in user settings
            val = self.user_settings[attr]  # 这里先去 用户自己的settings中去拿那么属性,如果能拿到就使用用户自己settings中的那个默认值
        except KeyError:
            # Fall back to defaults
            val = self.defaults[attr]  # 在用户的settings中拿不到,再去拿默认的配置的 属性;

        # Coerce import strings into classes
        if attr in self.import_strings:
            val = perform_import(val, attr)

        # Cache the result
        self._cached_attrs.add(attr)
        setattr(self, attr, val)
        return val

 settings中的defaults是一个大字典:

DEFAULTS = {
    # Base API policies
    DEFAULT_RENDERER_CLASSES: (
        rest_framework.renderers.JSONRenderer,
        rest_framework.renderers.BrowsableAPIRenderer,
    ),
    DEFAULT_PARSER_CLASSES: (
        rest_framework.parsers.JSONParser,
        rest_framework.parsers.FormParser,
        rest_framework.parsers.MultiPartParser
    ),
    """ ... 等等许多默认值 """ 
    
    },
}

通过这个方法,可以获得用户的settings中的配置:

@property
    def user_settings(self):
        if not hasattr(self, _user_settings):
            self._user_settings = getattr(settings, REST_FRAMEWORK, {})
        return self._user_settings

也就是说我们可以在settings中自己配置一个字典叫  REST_FRAMEWORK={},如:

REST_FRAMEWORK = {
    DEFAULT_PARSER_CLASSES: (
        # ‘rest_framework.parsers.JSONParser‘,
        rest_framework.parsers.FormParser,
        rest_framework.parsers.MultiPartParser
    ),
}

这么配置的话,我们的解析器就不能解析json格式的数据了。

over!

 

RestFramework之解析器源码

标签:lse   实例   父类   query   tor   没有   请求方式   raw   相对   

原文地址:https://www.cnblogs.com/glh-ty/p/9670339.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!