current position:Home>Django parser

Django parser

2022-01-31 13:28:13 Waiting for

This is my participation 11 The fourth of the yuegengwen challenge 11 God , Check out the activity details :2021 One last more challenge

One 、 Basic use

1.json Parser

Also take the order view as an example , add to json Parser , as follows :

from rest_framework.versioning import URLPathVersioning
from rest_framework.parsers import JSONParser
class UserView(APIView):
    ''' View user information '''
    parser_classes = [JSONParser,]
    versioning_class =URLPathVersioning

    def get(self,request,*args,**kwargs):
        res={"name":"wd","age":22}
        return JsonResponse(res,safe=True)
    
    
    def post(self,request,*args,**kwargs):
        print(request.data) # Get the parsed request result return JsonResponse({"success":"ok"}, safe=True)
 Copy code 

Use postman towards http://127.0.0.1:8000/api/v1/user View send json data , Note that the request header must be application/json, Here's the picture :

see post result ( The direct result is json Format ):

2.form Form parser

View

from rest_framework.versioning import URLPathVersioning
from rest_framework.parsers import JSONParser,FormParser
class UserView(APIView):
    ''' View user information '''
    parser_classes = [JSONParser,FormParser]
    ##JSONParser, Parse header information Content-Type:application/json, Of json data ##FormParser, Parse header information Content-Type:x-www-form-urlencoded data 
    versioning_class =URLPathVersioning

    def get(self,request,*args,**kwargs):
        res={"name":"wd","age":22}
        return JsonResponse(res,safe=True)


    def post(self,request,*args,**kwargs):
        print(request.data) # Get the parsed request result return JsonResponse({"success":"ok"}, safe=True)
 Copy code 

Use postman send out form The form data

Backstage acceptance , And the result has been transformed into QueryDict Type

 

Two 、 Source analysis

1. According to the above example , Sort out the data parsing process of the parser

  • Get user requests
  • Get user request body
  • According to the user request header information and parase_classes=[...], Compare the request headers in , Match the request header and use the parser to process
  • The parser takes data from the request body for processing , After processing, return the result to request.data

2. Source analysis :

The same as the permission source code process , Please come in , Execute first APIView Of dispatch Method , Here is the source code , Please see the notes for the analysis

dispatch Method :

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
        # Original request Carry on the processing , Enriched some functions 
        #Request(
        # request,
        # parsers=self.get_parsers(),
        # authenticators=self.get_authenticators(),
        # negotiator=self.get_content_negotiator(),
        # parser_context=parser_context
        # )
        #request( original request,[BasicAuthentications object ,])
        # Get native request,request._request
        # Get the object of the authentication class ,request.authticators
        #1. encapsulation request
        request = self.initialize_request(request, *args, **kwargs)
        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
 Copy code 

perform initialize_request() Method , In this method ,get_parsers Used to get the parser , And encapsulated in request.parsers in .

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

    return Request(
         equest,
        parsers=self.get_parsers(), # Get all parsers , Package to request.parsers in 
        authenticators=self.get_authenticators(),
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
    )
 Copy code 

get_parsers() Source code , And certification 、 The authority is the same , The parser uses list generation to return the list of parser objects , So the variables defining the parser in the example are parser_classes:

def get_parsers(self):
        """ Instantiates and returns the list of parsers that this view can use. """
        return [parser() for parser in self.parser_classes] # List generator , Returns the parser object 
 Copy code 

self.praser_classes, Default ( overall situation ) To configure

class APIView(View):

    # The following policies may be set at either globally, or per-view.
    renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
    parser_classes = api_settings.DEFAULT_PARSER_CLASSES  # Parser global configuration 
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
    throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
    permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
    content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
    metadata_class = api_settings.DEFAULT_METADATA_CLASS
    versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
 Copy code 

When calling request.data The parser will be used to get the requested data , Here is request.data Source code :

@property
def data(self):
   if not _hasattr(self, '_full_data'):
        self._load_data_and_files()   # perform _load_data_and_files(), Get request body data, get file data 
   return self._full_data
 Copy code 

perform self._load_data_and_files(), Get request data or file data ,self._load_data_and_files() Source code :

def _load_data_and_files(self):
        """ Parses the request content into `self.data`. """
        if not _hasattr(self, '_data'):
            self._data, self._files = self._parse()  # perform self_parse(), Get the parser , Also on content_type To analyze , Selector parser , Return the data 
            if self._files: # Judge file stream data , If it exists, add to self._full_data( That's our request.data) in 
                self._full_data = self._data.copy()    ,
                self._full_data.update(self._files)
            else:
                self._full_data = self._data  # There is no way to assign the parsed data without file stream to self._full_data(request.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
 Copy code 

perform self._prase() Method , Get the parser , And respond to the request Content-Type To analyze , Selector parser , Return the parsed data , Here are self._prase Source code :

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_type  #  Get... In the body of the request Content-Type
    try:
        stream = self.stream  #  If it's file data , Then get the file stream data 
    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
        # will already have been exhausted.
        if self._supports_form_parsing():
            return (self._request.POST, self._request.FILES)  #  Processing file type data 
        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)  #  Selector parser ,

    if not parser:
        raise exceptions.UnsupportedMediaType(media_type)

    try:
        parsed = parser.parse(stream, media_type,
                              self.parser_context)  #  Execute the parser parse Method ( It can be seen from here that every parser must have this method ), Parse the request 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)  #  Return parsing results , Yuan Zu , The parsed data is in parsed.data( stay load_data_and_files Use in self._data and self._files Acceptance ),                                 File data in parsed.files in 
    except AttributeError:
        empty_files = MultiValueDict()
        return (parsed, empty_files)
 Copy code 

That's the whole thing django rest framework Parser source code , Let's look at the example json Parser source code , Please refer to the notes for instructions :

class JSONParser(BaseParser):
    """ Parses JSON-serialized data. """
    media_type = 'application/json'   # Analytic Content-Type type 
    renderer_class = renderers.JSONRenderer
    strict = api_settings.STRICT_JSON

    def parse(self, stream, media_type=None, parser_context=None):  # Interpreted in the source code , This method is used to parse the request body 
        """ 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 None
            return json.load(decoded_stream, parse_constant=parse_constant)  # Essential use json Class to parse 
        except ValueError as exc:
            raise ParseError('JSON parse error - %s' % six.text_type(exc))
 Copy code 

3、 ... and 、 summary

1. Parser essence :

django rest framework The essence of parsing is based on Content-Type To achieve , Different types use different parsers , A view can have multiple parsers .

2. Use :

# Global use 
REST_FRAMEWORK = {
   
    # Parser "DEFAULT_PARSER_CLASSES":["rest_framework.parsers.JSONParser","rest_framework.parsers.FormParser"]
}

# Single view use 
parser_classes = [JSONParser,FormParser]
 Copy code 

\

copyright notice
author[Waiting for],Please bring the original link to reprint, thank you.
https://en.pythonmana.com/2022/01/202201311328104073.html

Random recommended