一,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!
原文地址:https://www.cnblogs.com/glh-ty/p/9670339.html