Django-drf架构 解析器的详解

Django-drf架构 解析器的详解

一、解析器作用:

解析器的作用就是服务器接收客户端传过来的数据,把数据解析自己想要的数据类型的过程

简单说就是对请求体中的数据进行解析

简单分析:

  • request.data将传来的Json数据解析成了字典的形式,我们可以使用request.data.get(‘请求参数’)来获取客户端传过来的值。我们知道原生的Django是不支付解析Json数据的,所以DRF将它完善。
  • REST framework 包含许多内置的解析器类,允许接受各种媒体类型(media types)的请求。

注意

  • 在开发客户端应用时,必须确保在HTTP请求中发送数据时设置Content-type,若不设置,大多数客户端将默认使用application/x-www-form-urlencoded,这并不是我们想要的,比如你使用ajax()方法发送json数据,应该确保包含contenType:'application/json'设置。

二、源码流程:

1.查看APIView中的dispatch发现,用initialize_request方法进行变身,实例化了一个新的request对象并赋值给request 。

  • 这个方法前面一部分将request对象传进了initialize_request方法进行二次封装,形成了一个新的request对象 :

    def dispatch(self, request, *args, **kwargs):
    	# 将request 进行第二次封装,返回一个新的request对象
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
    
  • 实例化了一个Request类的一个对象,进行返回,注意实例化过程中穿的参数:parsers(解析器的意思):

    def initialize_request(self, request, *args, **kwargs):
        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)
    
  • 实例化这个类:

    class Request(object):
        def __init__(self, request, parsers=None, authenticators=None,
                     		negotiator=None, parser_context=None):
    	    #可见,新的request._request就是原来的request对象,但是我们一般不用,因为后续还做了处理
             self._request = request 
             self.parsers = parsers or ()
    		# 使用这个方法就可以得到源类的request对象的GET数据
             @property
             def query_params(self):
                return self._request.GET
    

2.在新的request类中找到了request.data这个方法,这才是真正解析的开始:

  • Request类中的data方法,属性方法:

    @property
    def data(self):
        if not _hasattr(self, '_full_data'):
            self._load_data_and_files()
        return self._full_data
    
  • _ load_data_and_files中,有一个self._parse方法:

    def _load_data_and_files(self):
     if not _hasattr(self, '_data'):
         self._data, self._files = self._parse()
    
  • 找到这个_parse方法:

    def _parse(self):
    	# 【拿到请求头中的数据类型】根据请求头中的数据类型,对应不同的解析器进行解析
        media_type = self.content_type 
    	# 【self.parsers,选择解析器的步骤】
        parser = self.negotiator.select_parser(self, self.parsers)
        try:
    	   #  解析的过程
            parsed = parser.parse(stream, media_type, self.parser_context)
    
  • 选择解析器的过程中有一个self.parsers,这就回到了实例化的时候,返回查看:

    Request类的init中:self.parsers = parsers or ()
    # 此时的self是旧的,指的是自定义类的self某个对象
    实例化request对象时:parsers=self.get_parsers()
    
  • 自定义视图类的方法中,用了一个列表生成式,形成一个解析器对象的列表:

    def get_parsers(self):
        return [parser() for parser in self.parser_classes]
    
    # 返回到【上面的 _parse方法,看select_parser做了什么】
    def select_parser(self, request, parsers):
        for parser in parsers:
            if media_type_matches(parser.media_type, request.content_type):
                return parser
    
  • 返回到了最初的自定义视图类的perser_classes中:

    如果自己设置了就用自己的,自己没设置就用父类的。
    1,自己设置的情况可以理解。
    2,自己没设置,用父类的情况(APIView):
       # 默认的解析器,可以在settings.py 设置全局
       parser_classes = api_settings.DEFAULT_PARSER_CLASSES 
    	# 【APISettings实例化的对象】【DEFAULTS是配置文件里面的一个个键值对】
       api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS) 
       # 所以走了__getattr__方法
       def __getattr__(self, attr):
           try:
    		 # 执行了此函数,得出的应该是一个字典,且拿到了执行后的attr的对应值
               val = self.user_settings[attr] 
           except KeyError:
    		 #  全局找不到,就走默认
               val = self.defaults[attr]
    
       @property
       def user_settings(self):
           if not hasattr(self, '_user_settings'):
               self._user_settings = getattr(settings, 'REST_FRAMEWORK', {})
           return self._user_settings
    

总结

1.首先找自己视图类里面的:

class ParserView(APIView):
    parser_classes = [JSONParser]
	......逻辑......

2.找不到自己的,就去全局进行查找。

3.以上都不满足,就找默认的。

三、使用方式:

1.局部使用,可以基于类(APIView )的视图上设置单个视图或视图集的解析器 :

from rest_framework.parsers import JSONParser, FormParser
# 创建一个CBV
class ParserView(APIView):
	# 设置局部的解析器
    parser_classes = [JSONParser]
    """
    JSONParser: 表示只能解析 application/json 请求头
    FormParser: 表示只能解析 application/x-www-form-urlencoded 请求头
    """
    def post(self, request, *args, **kwargs):
        """
        允许用户发送JSON格式的数据
            a. content-type: application/json
            b. {"name": 'fe_cow', "age": 28}
        """
        """
        1. 获取用户请求
        2. 获取用户请求体
        3. 根据用户请求头和 parser_classes = [JSONParser, FormParser] 中支持的请求头进行比较
        4. JSONParser对象中去请求体
        5. request.data
        """
        print(request.data)
        return HttpResponse('Demo')

或FBV,@api_view 装饰器一起使用

from rest_framework.decorators import api_view
from rest_framework.decorators import parser_classes
from rest_framework.parsers import JSONParser

@api_view(['POST'])
@parser_classes((JSONParser,))
def example_view(request, format=None):
    """
    A view that can accept POST requests with JSON content.
    """
    return Response({'received data': request.data})

2.全局使用,可以使用 DEFAULT_PARSER_CLASSES 设置默认的全局解析器 :

# 全局使用,在settings.py中进行配置
REST_FRAMEWORK = {
"DEFAULT_PARSER_CLASSES":  ["rest_framework.JSONParser", "rest_framework.FormParser"]
}

四、API参考:

1.JSONParser

  • 解析Json请求内容
  • media_type:application/json

2.FormParser

  • 解析HTML表单内容
  • request.data是一个QueryDict字典,包含所有表单参数
  • 通常需要同时使用 FormParserMultiPartParser,以完全支持 HTML 表单数据
  • media_type:application/x-www-form-urlencoded

3.MultParParser

  • 解析文件上传时multipart HTML 表单内容
  • request.data 是一个 QueryDict(其中包含表单参数和文件)
  • 通常需要同时使用 FormParserMultiPartParser,以完全支持 HTML 表单数据
  • media_type:application/form-data

4.FileUploadParser

  • 解析文件上传内容

  • request.data 是一个 QueryDict (只包含一个存有文件的 'file' key)

  • 如果与 FileUploadParser 一起使用的视图是用 filename URL 关键字参数调用的,那么该参数将用作文件名

  • 如果在没有 filename URL 关键字参数的情况下调用,则客户端必须在 Content-Disposition HTTP header 中设置文件名。例如 Content-Disposition: attachment; filename=upload.jpg

  • media_type:*/*

  • 注意

    • FileUploadParser 用于本地客户端,可以将文件作为原始数据请求上传。对于基于 Web 的上传,或者对于具有分段上传支持的本地客户端,您应该使用 MultiPartParser 解析器。

    • 由于此解析器的 media_type 与任何 content type 都匹配,因此 FileUploadParser 通常应该是在 API 视图上设置的唯一解析器。

    • FileUploadParser 遵循 Django 的标准 FILE_UPLOAD_HANDLERS 设置和 request.upload_handlers 属性。

      # views.py
      class FileUploadView(views.APIView):
          parser_classes = (FileUploadParser,)
      
          def put(self, request, filename, format=None):
              file_obj = request.data['file']
              # ...
              # do some stuff with uploaded file
              # ...
              return Response(status=204)
      
      # urls.py
      urlpatterns = [
          # ...
          url(r'^upload/(?P[^/]+)$', FileUploadView.as_view())
      ]
      

你可能感兴趣的:(Django)