DRF installation configuration
What is the DRF
Abbreviated Django REST framework, mainly for the front and rear ends of the separate service, used to write api, provides a data interface to the front end.
Why should DRF
Even though we do not apply DRF, as can write RESTful interface to meet specifications, but may choose to use the DRF as a tool to improve the efficiency of development, not only because he was able to quickly help us design a compliant interface, but also provides the authority, certification and other powerful function.
DFF installation command
cnpm install djangoframework
DRF use
Import module, so that class inheritance APIView
class BookAPIView(APIView):
# 渲染模块的局部配置
# 局部禁用就是配置空list:[]
renderer_classes = [JSONRenderer, BrowsableAPIRenderer]
# 解析模块的局部配置
# parser_classes = [JSONParser, MultiPartParser, FormParser]
def get(self, request, *args, **kwargs):
print(request._request.GET)
print(request.GET)
print(request.META)
return Response({
'status': 0,
'msg': 'get ok'
})
def post(self, request, *args, **kwargs):
# print(request._request.POST)
# print(request.POST)
# print(request.META.get('HTTP_AUTH'))
# QueryDict转化为原生dict
# print(request.query_params.dict())
# print(type(request.data))
# if isinstance(request.data, QueryDict):
# request.data.dict()
print(request.data)
print(a)
return Response({
'status': 0,
'msg': 'post ok',
})
Source code analysis
drf we need to write a class to inherit APIView class, enter APIView look at the source code.
Because we want to call the way is the following (CBV)
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^books/$', views.BookAPIView.as_view()),
]
Direct call as_view (), so we just look at as_view method of APIView.
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
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
# Allow dependency injection of other settings to make testing easier.
settings = api_settings
schema = DefaultSchema()
@classmethod
def as_view(cls, **initkwargs):
"""
Store the original class on the view function.
This allows us to discover information about the view when we do URL
reverse lookups. Used for breadcrumb generation.
"""
# 这里判断 cls(我们写的继承了 APIView 的类)是否是queryset的子类,答案是“不是” ,所以这里就不用看他,直接看下面跳到下面的 2
if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
def force_evaluation():
raise RuntimeError(
'Do not evaluate the `.queryset` attribute directly, '
'as the result will be cached and reused between requests. '
'Use `.all()` or call `.get_queryset()` instead.'
)
cls.queryset._fetch_all = force_evaluation
# 2 这里调用了父类的 as_view方法。
view = super().as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs
# Note: session based authentication is explicitly CSRF validated,
# all other authentication is CSRF exempt.
return csrf_exempt(view)
There is no doubt inherited View APIView is written. We can see as_view method called as_view parent class, which consequently do not say, and certainly made some upgrades on the original method, can be seen from the comments, drf rewrite as_view do the only upgrade is "csrf_exempt (view)", so that view to avoid csrf check, visible drf feel csrf this check is not useful.
Into the parent method as_view
Then we enter this method and see what there is not the same (in fact the same, but find something is not the same).
@classonlymethod
def as_view(cls, **initkwargs):
"""
Main entry point for a request-response process.
"""
#这里对 initkwargs 做了一个遍历,但是没有传任何参数,所以直接跳过。
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key))
#这里是对 我们写的类的实例化对象进行赋值。
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
#最关键的一步就在这里。
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view
In the above code I demonstrated self.dispatch method is the most critical step is the drf the core part.
dispatch here must not be left with ctrl plus point in, because he will find View of dispatch, but in fact we drf write their own, that is, to have this method in APIView in accordance with the search order, we will first go to our own writing class there, and then go looking for parent APIView in, go to the View to find, so here is APIView found in the dispatch.
Into the dispatch method of APIView
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
# 对 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
This part of the code is drf the core code.
Step by step point of view, I look at the label on the request was the second package, look into the source code.
A request module fast
Enter self.initialize_request () method
Look into this method is how to achieve the request for secondary packaging.
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
# 这是解析模块,我们后面会讲。
parser_context = self.get_parser_context(request)
#这里返回了一个 Request类实例化的对象,把我们的request丢了进去。
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
Step must be to request the second package is necessarily Request () to do, and look at him instantiated object is kind of how.
Into the Request (), to see his init method
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__)
)
# 这里用 _request来保存了原生的request,完成了二次封装
self._request = request
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 = {}
self.parser_context['request'] = self
self.parser_context['encoding'] = request.encoding or settings.DEFAULT_CHARSET
force_user = getattr(request, '_force_auth_user', None)
force_token = getattr(request, '_force_auth_token', None)
if force_user is not None or force_token is not None:
forced_auth = ForcedAuthentication(force_user, force_token)
self.authenticators = (forced_auth,)
Then, whether you are or what request.b request.a casually point, it is not getattr method will take the request?
Look at his method getattr
def __getattr__(self, attr):
"""
If an attribute does not exist on this instance, then we also attempt
to proxy it to the underlying HttpRequest object.
"""
try:
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
He would go _request found inside to find, and then can not find it, go inside to find their own, __ getattribute__ method is written in C, in short, to return to their properties. So there is the following conclusions:
drf的请求模块
1、drf的request是在wsgi的request基础上再次封装
2、wsgi的request作为drf的request一个属性:_request
3、新的request对旧的request做了完全兼容
4、新的request对数据解析更规范化:所有的拼接参数都解析到query_params中,所有数据包数据都被解析到data中
query_params和data属于QueryDict类型,可以 .dict() 转化成原生dict类型
Small summary:
""" 源码分析
1、drf的APIView类:重写了as_view(),但主体逻辑还是调用父类View的as_view(),局部禁用了csrf认证
重点:所有继承drf的基本视图类APIView的视图类,都不在做csrf认证校验
2、drf的APIView类:重写了dispatch(),在内部对request进行了二次封装:self.initialize_request(request, *args, **kwargs)
内部核心:
走drf的Request初始化方法__init__:self._request = request
drf的Request的getter方法__getattr__:先从self._request反射取属性,没取到再冲drf的request中取
"""
"""
核心:request除了可以访问原wsgi协议的request所有内容,还可以访问 query_params、data
This is drf module processes the request, then talk about drf rendering module processes.
Second, the rendering module
""" drf的渲染模块(了解)
这个模块就是决定最后你是可以用浏览器来访问还是json等等
1、可以在视图类中通过renderer_classes类属性对该视图的数据响应渲染做配置 - 局部配置
2、可以在项目的配置文件的drf配置中通过DEFAULT_RENDERER_CLASSES对该视图的数据响应渲染做配置 - 全局配置
注:如果一个视图类在有全局配置下,还进行了局部配置,优先走自己的局部配置
"""
Source analysis:
This will not have to start from scratch to find, because previously talked about the most important part is to rewrite the dispatch. So start watching directly from the 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
# 对 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了
response = self.handle_exception(exc)
# 这里对response进行了二次处理响应,内部完成了多种结果的渲染方式。
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
The result has been the response and request thrown into this approach in secondary treatment, and back again.
Enter finalize_response () method
def finalize_response(self, request, response, *args, **kwargs):
"""
Returns the final response object.
"""
# Make the error obvious if a proper response is not returned
# 这是断言,判断你的response 是不是 HttpResponseBase的子类,断言通过的话就会继续往下走代码,没通过就会抛出异常,这里的意思就相当于是,如果你的视图函数没有返回 HttpResponse 类的对象,就会报错,也叫作响应基类。这里能通过,所以往下走。
assert isinstance(response, HttpResponseBase), (
'Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` '
'to be returned from the view, but received a `%s`'
% type(response)
)
# 这里继续走,因为我们的response 是Response的子类。
if isinstance(response, Response):
# 第二个参数代表是允许的渲染类,去request里面拿,默认我们是没有写的,所以这里会进这个if。
if not getattr(request, 'accepted_renderer', None):
#内部解析了配置的渲染类
neg = self.perform_content_negotiation(request, force=True)
#这一步是解压赋值,就是解压缩,前面接受的是允许的渲染类,后面接受的是允许的渲染头的类型,所以说上一步的 neg 一定会被赋值一个元组,而且元组里面一定会有一个渲染类,进去看一下这个方法做了什么 ,accepted_media_type里面就是表示到底是渲染 json 还是 标签 还是页面
request.accepted_renderer, request.accepted_media_type = neg
# 2 这里把一个一个东西都丢给了response,格式化他,然后要怎么渲染?这里是交给中间件来完成的,中间件会做这件事 response.accepted_renderer.render(response.renderer_context),来进行渲染,到此为止,再往下就会有很多很多东西,不用在看了。
response.accepted_renderer = request.accepted_renderer
response.accepted_media_type = request.accepted_media_type
#这个就是他的内容,有view,有args,有kwargs,和request请求相对应的信息。
response.renderer_context = self.get_renderer_context()
# Add new vary headers to the response instead of overwriting.
vary_headers = self.headers.pop('Vary', None)
if vary_headers is not None:
patch_vary_headers(response, cc_delim_re.split(vary_headers))
for key, value in self.headers.items():
response[key] = value
return response
Enter perform_content_negotiation () method
def perform_content_negotiation(self, request, force=False):
"""
Determine which renderer and media type to use render the response.
"""
#进入这个方法看一下 renderers 是什么东西。
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
Method entered get_renderers
def get_renderers(self):
"""
Instantiates and returns the list of renderers that this view can use.
"""
return [renderer() for renderer in self.renderer_classes]
The Scarlet Letter is a list of the first derivation, from self.renderer_classes get inside, eh, where you can and I started writing on the inside of a *** "by renderer_classes class properties on the view in the view class configuration data do render response - local configuration "***
He is how to find it, start to find their own, self here is to write our own class itself, and if we do not set it, it will go to the parent class, parent class, which is APIView in.
# 第一行就是,父类也是先去自己的配置文件中找,配置文件又先走自己的配置文件,然后再去找默认的配置文件。
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
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
Find Process: APIView class attribute (looking from the configuration file) from their own view class (local configuration)>> own project profile (global configuration)> drf weary profile
Just look at the back of the list comprehensions, behind renderer added a brace, this is the instance of the object is instantiated objects, this list is stored inside this is a rendering of a class.
Continue back to the top finalize_response () method seen in 2
Small summary:
""" 渲染模块源码分析
1、二次处理响应对象:APIView的dispatch方法 - self.finalize_response(request, response, *args, **kwargs)
2、获取渲染类对象:进入finalize_response方法 - self.perform_content_negotiation(request, force=True)
3、从配置文件中得到渲染类对象:perform_content_negotiation -> self.get_renderers() -> [renderer() for renderer in self.renderer_classes]
"""
"""
核心:可以全局和局部配置视图类支持的结果渲染:默认可以json和页面渲染,学习该模块的目的是开发可以全局只配置json方式渲染
"""
Analysis module
""" drf的解析模块(了解) - 服务的对象是数据包数据
这个模块的作用是来解析你传来的数据的,因为有可能你传来的是 formdata类型,也有可能是json类型,它需要解析。
1、可以在视图类中通过parser_classes类属性对该视图的数据包解析做配置 - 局部配置
2、可以在项目的配置文件的drf配置中通过DEFAULT_PARSER_CLASSES对该视图的数据包解析做配置 - 全局配置
"""
Where data is parsed to complete it?
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
# 对 request 进行了二次封装,就是在这里面完成的,然后丢尽了data和query_params里面,再次进入这个方法看一下
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
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
#准备要解析的内容,看一下这个方法返回的是什么。
parser_context = self.get_parser_context(request)
return Request(
#二次封装用的,已经讲过了
request,
#解析模块:在封装原生request时,将数据一并解析了,进入这个方法
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
def get_parser_context(self, http_request):
"""
Returns a dict that is passed through to Parser.parse(),
as the `parser_context` keyword argument.
"""
# Note: Additionally `request` and `encoding` will also be added
# to the context by the Request object.
return {
'view': self,
'args': getattr(self, 'args', ()),
'kwargs': getattr(self, 'kwargs', {})
}
So this is the method returns a dictionary. So parser_context is a dictionary of.
Enter get_parsers () method
def get_parsers(self):
"""
Instantiates and returns the list of parsers that this view can use.
"""
#看到这,就知道他能完成局部配置和全局配置
return [parser() for parser in self.parser_classes]
So, you can write in your own writing class inside
parser_classes = [JSONParser, MultiPartParser, FormParser], which will allow the parsed data json class, form-data, urlencoding.
# JSONParser: json数据
# FormParser: urlencoded
# MultiPartParser:form-data
As for where you can see it, in which parsers
from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
#drf默认配置了下面前三个
class JSONParser(BaseParser)
class FormParser(BaseParser)
class MultiPartParser(BaseParser)
class FileUploadParser(BaseParser)
Find the same order and rendering module. They can also configure their own settings in a file inside
REST_FRAMEWORK = {
# 渲染模块的全局配置:开发一般只配置json
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
],
# 解析模块的全局配置
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
],
# 异常模块
# 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
'EXCEPTION_HANDLER': 'api.utils.exception_handler',
}
Small summary
""" 解析模块源码分析
1、APIView的dispatch方法:self.initialize_request(request, *args, **kwargs)内部还提供了数据解析
2、self.get_parser_context(request)提供要解析的数据,self.get_parsers()提供解析的类对象(内部从配置中找解析类)
"""
"""
核心:请求的数据包格式会有三种(json、urlencoded、form-data),drf默认支持三种数据的解析,可以全局或局部配置视图类具体支持的解析方式
"""
So rendering module and analysis module is the most important local configuration and the global configuration. It will be configured on the line.
Abnormal module
""" 异常模块(重点):重写异常模块目的是记录异常信息(项目上线)
1、在settings的drf配置中配置EXCEPTION_HANDLER,指向自定义的exception_handler函数
2、drf出现异常了,都会回调exception_handler函数,携带 异常对象和异常相关信息内容,
在exception_handler函数完成异常信息的返回以及异常信息的logging日志
"""
Talk about how to use the exception model, then analyze the source code
Utils configure a file in your application, and then write a exception_handler.py file. This file is written inside exception_handle (exc, context) function, and then configure the settings inside, there is an exception when walking this function.
settings.py
REST_FRAMEWORK = {
# 渲染模块的全局配置:开发一般只配置json
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
],
# 解析模块的全局配置
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
],
# 异常模块
# 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
'EXCEPTION_HANDLER': 'api.utils.exception_handler',
}
The original version exception_handle (exc, context) Function
from rest_framework.response import Response
def exception_handler(exc, context):
# 开发阶段一定要记录日志
# logging.error(exc)
#因为只能返回字符串,所以要用这种形式来写
return Response('%s - %s' % (context['view'].__class__.__name__, exc))
Next source code analysis, we know, all entries are 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
#二次封装过了
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)
#捕获到了一个异常,命名为 exc
except Exception as exc:
#然后调用这个方法,我们配置的叫做 exception_handle,所以对应的还不是我们写的那个。 这个方法就是异常模块
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
Then we take a look handle_exception (exc) method
def handle_exception(self, exc):
"""
Handle any exception that occurs, by returning an appropriate response,
or re-raising the error.
"""
if isinstance(exc, (exceptions.NotAuthenticated,
exceptions.AuthenticationFailed)):
# WWW-Authenticate header for 401 responses, else coerce to 403
auth_header = self.get_authenticate_header(self.request)
if auth_header:
exc.auth_header = auth_header
else:
exc.status_code = status.HTTP_403_FORBIDDEN
#获取异常处理函数,进入这个函数看一下
exception_handler = self.get_exception_handler()
#这个就是内容,view 啊 args啊 kwargs啊这些的,和上面得到的异常对象一起丢给下面的异常处理函数,由于上面的那个步骤,这里的异常处理函数已经是我们自己写的那个 exception_handler了。
context = self.get_exception_handler_context()
#异常函数:接收 exc, context,返回response
response = exception_handler(exc, context)
#如果是response不是none,就代表drf自己处理了。那什么时候会为none呢?看下面,当我们不自己配,用drf的异常处理的时候会怎么样。
if response is None:
#交给中间件处理,原生django处理
self.raise_uncaught_exception(exc)
#告诉前台是异常返回
response.exception = True
return response
def get_exception_handler(self):
"""
Returns the exception handler that this view uses.
"""
#再熟悉不过了,你有,就找你的,没有就找系统的
return self.settings.EXCEPTION_HANDLER
When we do not own distribution, with exception handling drf of time, look at drf own exception_handler function
def exception_handler(exc, context):
"""
Returns the response that should be used for any given exception.
By default we handle the REST framework `APIException`, and also
Django's built-in `Http404` and `PermissionDenied` exceptions.
Any unhandled exceptions may return `None`, which will cause a 500 error
to be raised.
"""
#这里判断异常是不是 属于 404 这种类型的
if isinstance(exc, Http404):
exc = exceptions.NotFound()
#这里判断异常是不是关于权限的
elif isinstance(exc, PermissionDenied):
exc = exceptions.PermissionDenied()
#这里判断异常是不是属于基类的,就是drf最大的异常收集范围了,如果超出了这个,就说明你的异常比这个基类还要大,就是原生的异常了,类似于什么没定义就调用某个变量这种的。
if isinstance(exc, exceptions.APIException):
headers = {}
if getattr(exc, 'auth_header', None):
headers['WWW-Authenticate'] = exc.auth_header
if getattr(exc, 'wait', None):
headers['Retry-After'] = '%d' % exc.wait
if isinstance(exc.detail, (list, dict)):
data = exc.detail
else:
data = {'detail': exc.detail}
set_rollback()
#如果处理的了的话,就返回一个response对象
return Response(data, status=exc.status_code, headers=headers)
#上面这个过程就相当于是drf在处理他能处理的范围,超出了能处理的范围的话,就返回none
#处理不了的时候就返回none,让django来处理。
return None
These are handled when drf
Next, look at what we have to deal with their own
Upgraded version of the custom exception handling
升级版
from rest_framework.response import Response
from rest_framework.views import exception_handler as drf_exception_handler
def exception_handler(exc, context):
#默认是drf可以解决的异常
response = drf_exception_handler(exc, context)
#当drf不能解决时,就会是none,这时候就直接返回一个响应
if response is None: # drf没有处理的异常
response = Response({'detail': '%s' % exc})
# 项目阶段,要记录到日志文件
return response
Small summary
""" 源码分析
1、在APIView的dispatch方法中,有一个超大的try...except...,将代码运行异常都交给异常处理模块处理self.handle_exception(exc)
2、从配置中映射出配置处理异常的函数(自定义异常模块就是自定义配置指向自己的函数):self.get_exception_handler()
3、异常函数exception_handler(exc, context)处理异常,就会走自己的:
先交给系统处理(客户端的异常),系统没处理(服务器异常),再自己处理
"""
"""
核心:异常信息都需要被logging记录,所以需要自定义;drf只处理客户端异常,服务器异常需要手动处理,统一处理结果
"""
Response module
Response module is actually a response, so go directly to the source code like a response
class Response(SimpleTemplateResponse):
"""
An HttpResponse that allows its data to be rendered into
arbitrary media types.
"""
#从升级版可以看出,这里的data就是你传进来的exc,status就是状态码
def __init__(self, data=None, status=None,
template_name=None, headers=None,
exception=False, content_type=None):
"""
Alters the init arguments slightly.
For example, drop 'template_name', and instead use 'data'.
Setting 'renderer' and 'media_type' will typically be deferred,
For example being set automatically by the `APIView`.
"""
super().__init__(None, status=status)
if isinstance(data, Serializer):
msg = (
'You passed a Serializer instance as data, but '
'probably meant to pass serialized `.data` or '
'`.error`. representation.'
)
raise AssertionError(msg)
self.data = data
self.template_name = template_name
self.exception = exception
self.content_type = content_type
if headers:
for name, value in headers.items():
self[name] = value
So why should we come up with a final version, because the previous version you wrong, status code is 200, it does not, so to him myself configuration response status code.
Final version:
""" 究极版
response = {
'status': 7,
'exc': '异常信息'
}
"""
from rest_framework.response import Response
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework import status
def exception_handler(exc, context):
response = drf_exception_handler(exc, context)
if response is None: # drf没有处理的异常(服务器异常)
#第二个参数就是500,
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR, data={
'status': 7,
'exc': '%s' % exc
})
# 项目阶段,要记录到日志文件
return Response(status=response.status_code, data={
'status': 7,
# drf处理的客户端异常,原始处理方式是将异常信息放在response对象的data中,data的格式是{'datail': '具体的异常信息'}
'exc': '%s' % response.data.get('detail')
})
Well, the end.