欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 时评 > Django Rest Framework -解析器

Django Rest Framework -解析器

2024/10/24 13:28:51 来源:https://blog.csdn.net/qq_61532016/article/details/141022757  浏览:    关键词:Django Rest Framework -解析器
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.parsers import JSONParserclass TestView(APIView):# JSONParser:请求头content-type为application/json# FormParser:请求头content-type为application/x-www-form-urlencoded# MultiPartParser: 请求头content-type为multipart/form-data#  FileUploadParser:上传文件parser_classes = [JSONParser, FormParser, MultiPartParser, FileUploadParser, ]def post(self, request, *args, **kwargs):print(request.content_type)# 获取请求的值,并使用对应的JSONParser进行处理print(request.data)# application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值print(request.POST)print(request.FILES)return Response('POST请求,响应内容')def put(self, request, *args, **kwargs):return Response('PUT请求,响应内容')

parser_classes属性变量中的值,是各种解析器对象。前端会向后台发送不同类型的请求,而django后台的drf接口必须通过配置解析器才能获取到相关请求数据。常用的解析器主要是“JSONParser”和“FormParser”这两个解析器。

源码分析

  
  def initialize_request(self, request, *args, **kwargs):"""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)def get_parsers(self):"""Instantiates and returns the list of parsers that this view can use."""return [parser() for parser in self.parser_classes]

同样还是从APIView类的入口方法dispatch入口,在dispatch方法中,调用了initialize_request方法。这个方法在前文中已经说过,是用来封装django原生的request请求的。由上可知,django原生的request请求数据被封装到了Request对象中,在实例化该对象时,将解析数据的解析器初始化到了parsers属性变量中。跳转到get_parsers方法中,可以看到,同样也是返回的是列表生成式。

class APIView(View):
​# The following policies may be set at either globally, or per-view.renderer_classes = api_settings.DEFAULT_RENDERER_CLASSESparser_classes = api_settings.DEFAULT_PARSER_CLASSESauthentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSESthrottle_classes = api_settings.DEFAULT_THROTTLE_CLASSESpermission_classes = api_settings.DEFAULT_PERMISSION_CLASSEScontent_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASSmetadata_class = api_settings.DEFAULT_METADATA_CLASSversioning_class = api_settings.DEFAULT_VERSIONING_CLASS
​# Allow dependency injection of other settings to make testing easier.settings = api_settings
​schema = DefaultSchema()

再跳转回APIView类中,可以看到属性变量parser_classes定义的地方。同样也可以通过settings配置文件进行全局配置。如果不需要进行全局配置,那就要在自定义的视图类中对parser_classes属性变量进行重新赋值,即:“parser_classes = [JSONParser, FormParser, MultiPartParser, FileUploadParser,]”。这列表元素都是解析器对象,只要这样配置好,drf就会根据parser_classes中的解析器去解析数据。

为什么从request.data中获取数据?

看到这里,心中有了疑惑,Django Rest Framework框架是在那里触发解析器这个功能的,在上面的源码分析中并未调用任何一个关于解析相关功能的方法,只是做了一个读取配置文件,封装新的request对象的过程,所以是在我们需要读数据的时候才会去触发解析器这个功能,所以我们从request.data入手,我们知道,django原生的request请求,会被drf通过Request对象封装,那么就跳转到Request类定义中看看这个“data”的实现。在data方法的实现中可以看到前端请求的数据是通过“load_data_and_files”方法获取的,并且返回值是“full_data”属性变量。先跳转到“_load_data_and_files”方法中:

@propertydef data(self):if not _hasattr(self, '_full_data'):self._load_data_and_files()return self._full_datadef _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
​# 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.POSTself._request._files = self.FILES

在“load_data_and_files”方法中可知,是通过调用“parse”方法去获取请求数据的,并且将请求数据保存在“data”和“files”属性变量中。除文件相关的数据外的数据都保存在了“data”中,而“data”中的数据又都赋值到“full_data”属性变量中。又由上一张图可知,“data”方法的返回值是“full_data”,即:data方法获取到的数据就是“_full_data”中的数据。

    
def _parse(self):"""Parse the request content, returning a two-tuple of (data, files)
​May raise an `UnsupportedMediaType`, or `ParseError` exception."""media_type = self.content_typetry:stream = self.streamexcept 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# will already have been exhausted.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)
​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)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._dataraise
​# 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)@propertydef content_type(self):meta = self._request.METAreturn meta.get('CONTENT_TYPE', meta.get('HTTP_CONTENT_TYPE', ''))

现在进一步分析_parse,首先第一步做的是获取请求头,可以看到将获取到的请求头保存到了“media_type”变量中。然后又通过调用“select_parser(self, self.parsers)”方法,来选择所需要的解析器(参数“self.parsers”就是我们在自定义视图中“parser_classes”的值)。

    
def select_parser(self, request, parsers):"""Given a list of parsers and a media type, return the appropriateparser to handle the incoming request."""for parser in parsers:if media_type_matches(parser.media_type, request.content_type):return parserreturn None
class JSONParser(BaseParser):"""Parses JSON-serialized data."""media_type = 'application/json'renderer_class = renderers.JSONRendererstrict = api_settings.STRICT_JSON
​def parse(self, stream, media_type=None, parser_context=None):"""Parses the incoming bytestream as JSON and returns the resulting data."""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 Nonereturn json.load(decoded_stream, parse_constant=parse_constant)except ValueError as exc:raise ParseError('JSON parse error - %s' % str(exc))

又通过,parser_classes中解析器对象中的parser方法来解析请求到的数据,即:“parsed = parser.parse(steam, media_type, self.parser_context)”。这里以“JSONParser”对象为例,由上可知,media_type属性变量中保存的是该解析器所对应的请求头,self.parser_context中,保存的是从前端请求中获取到的请求头。

再来到“JSONParser”类的“parse”方法中可知,再通过返回“json.load”来处理请求数据。返回的请求数据就会保存到“Request”类的“parse”方法中的parsed变量中。而parse方法的返回值为元组,第一个元素就是需要的数据。这些数据会在“load_data_and_files”方法中,赋值给“Request”类的“data”属性方法,而data中的数据会赋值给“full_data”中。因此,“Request”类中“data”方法返回值是“_full_data”,这样,就可以通过“request.data”获取请求数据。

全局配置

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

 配置加入不同的解析器,就会解析不同类型的请求数据,当然是可以同时配置加入多个解析器的。同样的,配置了全局解析器后,那么,在自定义的视图类中,就可以不用通过“parser_classes”属性变量进行添加解析器的。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com