DRF- django_rest_framework解析器基本使用及源码剖析

136 阅读4分钟

解析器

基本使用

from rest_framework.parsers import JSONParser,FormParser
from rest_framework.negotiation import DefaultContentNegotiation
    class parser(APIView):
    parser_classes = [JSONParser,FormParser]#所有的解析器
    content_negotiation_class = DefaultContentNegotiation#根据请求,匹配对应的解析器

    def post(self,request):
        #请求体中的参数会解析并整合到request.data
        print(request.data)
        # self.dispatch()
        return Response({'cie':21})

源码流程

## 6 ##
class Request:
    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        assert isinstance(request, HttpRequest), (
            'The `request` argument must be an instance of '
            '`django.http.HttpRequest`, not `{}.{}`.'
            .format(request.__class__.__module__, request.__class__.__name__)
        )

        self._request = request
        ## 6.1 ## 将 parsers,negotiator赋值给self.parsers,self.negotiator
        self.parsers = parsers or ()	#如果没有值则为空元组
        self.authenticators = authenticators or ()
        self.negotiator = negotiator or self._default_negotiator()
        
        self.parser_context = parser_context
        self._data = Empty
        self._files = Empty
        self._full_data = Empty
        self._content_type = Empty
        self._stream = Empty

        if self.parser_context is None:
            self.parser_context = {}
        ## 6.2 ## 	parser_context又新建了两个键:现在的self.parser_context--> {视图对象,URL路由参数,drf的request,encoding}
        self.parser_context['request'] = self
        self.parser_context['encoding'] = request.encoding or settings.DEFAULT_CHARSET

class APIView():
    
	def perform_content_negotiation(self, request, force=False):
        ## 7.1 ##
        renderers = self.get_renderers()			#渲染器,视图以什么方式展示,与解析器无关,先跳过
        conneg = self.get_content_negotiator()

        try:
            return conneg.select_renderer(request, renderers, self.format_kwarg)
        except Exception:
            if force:
                return (renderers[0], renderers[0].media_type)
            raise
            
    ## 7 ##
    def initial(self, request, *args, **kwargs):	
        self.format_kwarg = self.get_format_suffix(**kwargs)

        ## 7.1 ##  渲染器self.perform_content_negotiation
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg

        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        self.perform_authentication(request)
        self.check_permissions(request)
        self.check_throttles(request)
	def get_content_negotiator(self):
        ##5.3##实例化self.content_negotiation_class()
        if not getattr(self, '_negotiator', None):
            self._negotiator = self.content_negotiation_class()
        return self._negotiator
    def get_parsers(self):
        ## 5.2 ##循环获取并实例化self.parser_classes中的对象
        return [parser() for parser in self.parser_classes]
	def get_parser_context(self, http_request):
		## 3 ##返回的是一个字典
        return {
            'view': self,							#当前的对象
            'args': getattr(self, 'args', ()),		#dispatch中的self.args = args
            'kwargs': getattr(self, 'kwargs', {})	#dispatch中的self.kwargs = kwargs
        }
	def initialize_request(self, request, *args, **kwargs):
		##2##	调用self.get_parser_context,返回值是视图对象,URL路由参数
        parser_context = self.get_parser_context(request)
		
        ## 4 ##实例化一个Request
        return Request(
            request,								#django的request
            ## 5.1 ##读取所有的解析器的对象
            parsers=self.get_parsers(),				#解析器的对象	[JSONParser,FormParser]
            authenticators=self.get_authenticators(),	#认证组件,形式是[对象1,对象2,..]
            negotiator=self.get_content_negotiator(),	#返回DefaultContentNegotiation()
            parser_context=parser_context				#上方的字典{视图对象,URL路由参数}
        )
    def dispatch(self, request, *args, **kwargs):
            self.args = args
            self.kwargs = kwargs
            ##1## 在initialize_request中对请求进行处理
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
            self.headers = self.default_response_headers  # deprecate?

            try:
                ## 7 ##
                self.initial(request, *args, **kwargs)
                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

在initialize_request中只是帮我们实例化了解析器,那什么时候才会解析请求体中的内容呢?

当我们调用request.data时就会进行解析。又假如我们调用两次request.data那又会解析几次呢?

来看看源码怎么说的

## 4 ##	JSONParser解析器
class JSONParser(BaseParser):
    media_type = 'application/json'
    renderer_class = renderers.JSONRenderer
    strict = api_settings.STRICT_JSON
    def parse(self, stream, media_type=None, parser_context=None):
        parser_context = parser_context or {}
        encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
        try:
            decoded_stream = codecs.getreader(encoding)(stream)
            parse_constant = json.strict_constant if self.strict else None
            return json.load(decoded_stream, parse_constant=parse_constant)	#josn.load
        except ValueError as exc:
            raise ParseError('JSON parse error - %s' % str(exc))
            
## 3 ## 寻找解析器
class DefaultContentNegotiation(BaseContentNegotiation):
    settings = api_settings

    def select_parser(self, request, parsers):			#循环所有的解析器对象
        for parser in parsers:							#匹配content_type和media_type
            if media_type_matches(parser.media_type, request.content_type):
                return parser							#返回匹配成功的解析器	
        return None
    
class Response():
        def _parse(self):
		## 2 ## 解析流程
        media_type = self.content_type #content_type就是请求里的content_type,在本段代码最下端
        try:
            stream = self.stream		#请求的原始二进制数据
        except RawPostDataException:
            if not hasattr(self._request, '_post'):
                raise
            # If request.POST has been accessed in middleware, and a method='POST'
            # request was made with 'multipart/form-data', then the request stream
            if self._supports_form_parsing():
                return (self._request.POST, self._request.FILES)
            stream = None

        if stream is None or media_type is None:
            if media_type and is_form_media_type(media_type):
                empty_data = QueryDict('', encoding=self._request._encoding)
            else:
                empty_data = {}
            empty_files = MultiValueDict()
            return (empty_data, empty_files)
		## 3 ##  获取解析器,negotiator就是DefaultContentNegotiation(),会寻找每一个解析器的media_type与请求的content_type对比
        parser = self.negotiator.select_parser(self, self.parsers)
	
        if not parser:					#如果没有支持请求的类型的解析器,抛出异常
            raise exceptions.UnsupportedMediaType(media_type)

        try:							#有匹配的解析器,执行parser.parse()
            parsed = parser.parse(stream, media_type, self.parser_context)
        	## 4 ##JSONParser解析器->json.load不是loads,因为请求的数据不是字符串,loads是处理字符串的,请求的二进制数据存储在内存中,可以read()读取,相当于一个文件对象,使用load()	
            #FormParser解析器—>解析 name=asd&password=213 解析成QueryDict(django提供的)
    	except Exception:
            self._data = QueryDict('', encoding=self._request._encoding)
            self._files = MultiValueDict()
            self._full_data = self._data
            raise
		try:		#返回解析器的成员变量data和files
            return (parsed.data, parsed.files)
        except AttributeError:
            empty_files = MultiValueDict()
            return (parsed, empty_files)

            
    def _load_data_and_files(self):
		## 1.1 ##
        if not _hasattr(self, '_data'):					#有无self._data
            self._data, self._files = self._parse()		## 2 ## 将_data解析
            if self._files:
                self._full_data = self._data.copy()		#解析过一次后,第二次解析将直接返回self._full_data
                self._full_data.update(self._files)
            else:
                self._full_data = self._data

            # if a form media type, copy data & files refs to the underlying
            # http request so that closable objects are handled appropriately.
            if is_form_media_type(self.content_type):
                self._request._post = self.POST
                self._request._files = self.FILES

    @property
    def data(self):
        if not _hasattr(self, '_full_data'):		#反射执行,self._full_data
            ## 1 ##
            self._load_data_and_files()				#如果没有则self._load_data_and_files()
        return self._full_data						#有则返回解析后的数据
    @property
    def content_type(self):
        #请求头信息
        meta = self._request.META
        return meta.get('CONTENT_TYPE', meta.get('HTTP_CONTENT_TYPE', ''))

文件的上传

drf上传文件.png

默认的解析器

print(self.parser_classes)

#[<class 'rest_framework.parsers.JSONParser'>, <class 'rest_framework.parsers.FormParser'>, <class 'rest_framework.parsers.MultiPartParser'>]
#MultiPartParser    既可以上传文件又能上传json

修改全局配置

REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES':['rest_framework.parsers.JSONParser','rest_framework.parsers.FormParser',]
}