Django REST framework 总结(附源码剖析)
Django 的 CBV&FBV
Django FBV, function base view 视图里使用函数处理请求
url
- url(r‘^users/‘, views.users),
views
- from django.shortcuts import HttpResponse
- import json
- def users(request):
- user_list = ['lcg','superman']
- return HttpResponse(json.dumps((user_list)))
Django CBV, class base view 视图里使用类处理请求
url
- url(r'^students/', views.StudentsView.as_view()),
views
- from django.shortcuts import HttpResponse
- from django.views import View
- class StudentsView(View):
- def get(self,request,*args,**kwargs):
- return HttpResponse('GET')
- def post(self, request, *args, **kwargs):
- return HttpResponse('POST')
- def put(self, request, *args, **kwargs):
- return HttpResponse('PUT')
- def delete(self, request, *args, **kwargs):
- return HttpResponse('DELETE')
注意:
- cbv定义类的时候必须要继承view
- 在写url的时候必须要加as_view
- 类里面使用form表单提交的话只有get和post方法
- 类里面使用ajax发送数据的话支持定义以下很多方法
restful规范:
'get'获取数据, 'post'创建新数据, 'put'更新, 'patch'局部更新, 'delete'删除, 'head', 'options', 'trace'
CBV原理:继承,反射。
点进StudentView继承的View可以看到如下代码
- class View(object):
- """
- Intentionally simple parent class for all views. Only implements
- dispatch-by-method and simple sanity checking.
- """
- http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
- def __init__(self, **kwargs):
- """
- Constructor. Called in the URLconf; can contain helpful extra
- keyword arguments, and other things.
- """
- # Go through keyword arguments, and either save their values to our
- # instance, or raise an error.
- for key, value in six.iteritems(kwargs):
- setattr(self, key, value)
- @classonlymethod
- def as_view(cls, **initkwargs):
- """
- Main entry point for a request-response process.
- """
- 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
- def dispatch(self, request, *args, **kwargs):
- # Try to dispatch to the right method; if a method doesn't exist,
- # defer to the error handler. Also defer to the error handler if the
- # request method isn't on the approved list.
- 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
- return handler(request, *args, **kwargs)
- def http_method_not_allowed(self, request, *args, **kwargs):
- logger.warning(
- 'Method Not Allowed (%s): %s', request.method, request.path,
- extra={'status_code': 405, 'request': request}
- )
- return http.HttpResponseNotAllowed(self._allowed_methods())
- def options(self, request, *args, **kwargs):
- """
- Handles responding to requests for the OPTIONS HTTP verb.
- """
- response = http.HttpResponse()
- response['Allow'] = ', '.join(self._allowed_methods())
- response['Content-Length'] = '0'
- return response
- def _allowed_methods(self):
- return [m.upper() for m in self.http_method_names if hasattr(self, m)]
View类里面的部分如下:
- @classonlymethod
- def as_view(cls, **initkwargs):
- """
- Main entry point for a request-response process.
- """
- 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
上面实质上是路由里面的那里写的as_view ,返回值是view 而view方法返回的是self.dispath。
- http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
该列表定义了八种客户端对服务端的访问方式
从路由到视图这个过程,会自动初始化该StudentView对象,并执行在类里写好的诸多方法,其中较为关键的是dispatch方法(通过as_view)。
- def dispatch(self, request, *args, **kwargs):
- # Try to dispatch to the right method; if a method doesn't exist,
- # defer to the error handler. Also defer to the error handler if the
- # request method isn't on the approved list.
- 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
- return handler(request, *args, **kwargs)
函数主要是对客户端发来的请求进行分类处理,按照对应的方法去处理,也就是我们在FBV函数里经常写到的request.method。
也就是说,继承自View的类下的所有的方法本质上都是通过dispatch这个函数反射执行,如果想要在执行get或post方法前执行其他步骤,可以重写dispatch。
Django REST framework CBV
url
- url(r'^students/', views.StudentsView.as_view()),
views
- from rest_framework.views import APIView
- from django.shortcuts import HttpResponse
- class StudentsView(APIView):
- def get(self, request, *args, **kwargs):
- return HttpResponse('GET')
- def post(self, request, *args, **kwargs):
- return HttpResponse('POST')
- def put(self, request, *args, **kwargs):
- return HttpResponse('PUT')
- def delete(self, request, *args, **kwargs):
- return HttpResponse('DELETE')
rest_framework.views里面导入的APIView实际上是对Django的View的一个封装
Django REST framework CBV的原理与Django的原理是一样的。路由里面的那里写的as_view ,返回值是view 而view方法返回的是self.dispath。APIView基础了View,并且重写了dispath方法。APIView中的dispatch方法有很多的功能
- 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.
- """
- 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
- view = super(APIView, cls).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)
- @property
- def allowed_methods(self):
- """
- Wrap Django's private `_allowed_methods` interface in a public property.
- """
- return self._allowed_methods()
- @property
- def default_response_headers(self):
- headers = {
- 'Allow': ', '.join(self.allowed_methods),
- }
- if len(self.renderer_classes) > 1:
- headers['Vary'] = 'Accept'
- return headers
- def http_method_not_allowed(self, request, *args, **kwargs):
- """
- If `request.method` does not correspond to a handler method,
- determine what kind of exception to raise.
- """
- raise exceptions.MethodNotAllowed(request.method)
- def permission_denied(self, request, message=None):
- """
- If request is not permitted, determine what kind of exception to raise.
- """
- if request.authenticators and not request.successful_authenticator:
- raise exceptions.NotAuthenticated()
- raise exceptions.PermissionDenied(detail=message)
- def throttled(self, request, wait):
- """
- If request is throttled, determine what kind of exception to raise.
- """
- raise exceptions.Throttled(wait)
- def get_authenticate_header(self, request):
- """
- If a request is unauthenticated, determine the WWW-Authenticate
- header to use for 401 responses, if any.
- """
- authenticators = self.get_authenticators()
- if authenticators:
- return authenticators[0].authenticate_header(request)
- 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', {})
- }
- def get_renderer_context(self):
- """
- Returns a dict that is passed through to Renderer.render(),
- as the `renderer_context` keyword argument.
- """
- # Note: Additionally 'response' will also be added to the context,
- # by the Response object.
- return {
- 'view': self,
- 'args': getattr(self, 'args', ()),
- 'kwargs': getattr(self, 'kwargs', {}),
- 'request': getattr(self, 'request', None)
- }
- def get_exception_handler_context(self):
- """
- Returns a dict that is passed through to EXCEPTION_HANDLER,
- as the `context` argument.
- """
- return {
- 'view': self,
- 'args': getattr(self, 'args', ()),
- 'kwargs': getattr(self, 'kwargs', {}),
- 'request': getattr(self, 'request', None)
- }
- def get_view_name(self):
- """
- Return the view name, as used in OPTIONS responses and in the
- browsable API.
- """
- func = self.settings.VIEW_NAME_FUNCTION
- return func(self.__class__, getattr(self, 'suffix', None))
- def get_view_description(self, html=False):
- """
- Return some descriptive text for the view, as used in OPTIONS responses
- and in the browsable API.
- """
- func = self.settings.VIEW_DESCRIPTION_FUNCTION
- return func(self.__class__, html)
- # API policy instantiation methods
- def get_format_suffix(self, **kwargs):
- """
- Determine if the request includes a '.json' style format suffix
- """
- if self.settings.FORMAT_SUFFIX_KWARG:
- return kwargs.get(self.settings.FORMAT_SUFFIX_KWARG)
- def get_renderers(self):
- """
- Instantiates and returns the list of renderers that this view can use.
- """
- return [renderer() for renderer in self.renderer_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]
- def get_authenticators(self):
- """
- Instantiates and returns the list of authenticators that this view can use.
- """
- return [auth() for auth in self.authentication_classes]
- def get_permissions(self):
- """
- Instantiates and returns the list of permissions that this view requires.
- """
- return [permission() for permission in self.permission_classes]
- def get_throttles(self):
- """
- Instantiates and returns the list of throttles that this view uses.
- """
- return [throttle() for throttle in self.throttle_classes]
- def get_content_negotiator(self):
- """
- Instantiate and return the content negotiation class to use.
- """
- if not getattr(self, '_negotiator', None):
- self._negotiator = self.content_negotiation_class()
- return self._negotiator
- def get_exception_handler(self):
- """
- Returns the exception handler that this view uses.
- """
- return self.settings.EXCEPTION_HANDLER
- # API policy implementation methods
- def perform_content_negotiation(self, request, force=False):
- """
- Determine which renderer and media type to use render the response.
- """
- 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
- def perform_authentication(self, request):
- """
- Perform authentication on the incoming request.
- Note that if you override this and simply 'pass', then authentication
- will instead be performed lazily, the first time either
- `request.user` or `request.auth` is accessed.
- """
- request.user
- def check_permissions(self, request):
- """
- Check if the request should be permitted.
- Raises an appropriate exception if the request is not permitted.
- """
- for permission in self.get_permissions():
- if not permission.has_permission(request, self):
- self.permission_denied(
- request, message=getattr(permission, 'message', None)
- )
- def check_object_permissions(self, request, obj):
- """
- Check if the request should be permitted for a given object.
- Raises an appropriate exception if the request is not permitted.
- """
- for permission in self.get_permissions():
- if not permission.has_object_permission(request, self, obj):
- self.permission_denied(
- request, message=getattr(permission, 'message', None)
- )
- def check_throttles(self, request):
- """
- Check if request should be throttled.
- Raises an appropriate exception if the request is throttled.
- """
- for throttle in self.get_throttles():
- if not throttle.allow_request(request, self):
- self.throttled(request, throttle.wait())
- def determine_version(self, request, *args, **kwargs):
- """
- If versioning is being used, then determine any API version for the
- incoming request. Returns a two-tuple of (version, versioning_scheme)
- """
- if self.versioning_class is None:
- return (None, None)
- scheme = self.versioning_class()
- return (scheme.determine_version(request, *args, **kwargs), scheme)
- # Dispatch methods
- 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 initial(self, request, *args, **kwargs):
- """
- Runs anything that needs to occur prior to calling the method handler.
- """
- self.format_kwarg = self.get_format_suffix(**kwargs)
- # Perform content negotiation and store the accepted info on the request
- neg = self.perform_content_negotiation(request)
- request.accepted_renderer, request.accepted_media_type = neg
- # Determine the API version, if versioning is in use.
- version, scheme = self.determine_version(request, *args, **kwargs)
- request.version, request.versioning_scheme = version, scheme
- # Ensure that the incoming request is permitted
- self.perform_authentication(request)
- self.check_permissions(request)
- self.check_throttles(request)
- def finalize_response(self, request, response, *args, **kwargs):
- """
- Returns the final response object.
- """
- # Make the error obvious if a proper response is not returned
- assert isinstance(response, HttpResponseBase), (
- 'Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` '
- 'to be returned from the view, but received a `%s`'
- % type(response)
- )
- if isinstance(response, Response):
- if not getattr(request, 'accepted_renderer', None):
- neg = self.perform_content_negotiation(request, force=True)
- request.accepted_renderer, request.accepted_media_type = neg
- response.accepted_renderer = request.accepted_renderer
- response.accepted_media_type = request.accepted_media_type
- 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
- 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()
- context = self.get_exception_handler_context()
- response = exception_handler(exc, context)
- if response is None:
- self.raise_uncaught_exception(exc)
- response.exception = True
- return response
- def raise_uncaught_exception(self, exc):
- if settings.DEBUG:
- request = self.request
- renderer_format = getattr(request.accepted_renderer, 'format')
- use_plaintext_traceback = renderer_format not in ('html', 'api', 'admin')
- request.force_plaintext_errors(use_plaintext_traceback)
- raise
- # Note: Views are made CSRF exempt from within `as_view` as to prevent
- # accidental removal of this exemption in cases where `dispatch` needs to
- # be overridden.
- 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)
- except Exception as exc:
- response = self.handle_exception(exc)
- self.response = self.finalize_response(request, response, *args, **kwargs)
- return self.response
- def options(self, request, *args, **kwargs):
- """
- Handler method for HTTP 'OPTIONS' request.
- """
- if self.metadata_class is None:
- return self.http_method_not_allowed(request, *args, **kwargs)
- data = self.metadata_class().determine_metadata(request, self)
- return Response(data, status=status.HTTP_200_OK)
APIView类源代码
- class APIView(View):
- 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
- # 第三步、执行:get/post/put/delete函数
- 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
下面将对dispatch完成的功能进行详细的分析
Django REST framework 总结(附源码剖析)的更多相关文章
- 使用MiniProfiler给Asp.net MVC和Entity Framework号脉(附源码)
在学习python开发框架pylons/pyramid的过程中,里面有个非常棒的页面性能监控功能,这样在开发过程中,你能清楚的知道当前页面的性能以及其它参数. 这里介绍一下如何给Asp.net MVC ...
- Django REST framework —— 权限组件源码分析
在上一篇文章中我们已经分析了认证组件源码,我们再来看看权限组件的源码,权限组件相对容易,因为只需要返回True 和False即可 代码 class ShoppingCarView(ViewSetMix ...
- Django REST framework —— 认证组件源码分析
我在前面的博客里已经讲过了,我们一般编写API的时候用的方式 class CoursesView(ViewSetMixin,APIView): pass 这种方式的有点是,灵活性比较大,可以根据自己的 ...
- Django rest framework框架——APIview源码分析
一.什么是rest REST其实是一种组织Web服务的架构,而并不是我们想象的那样是实现Web服务的一种新的技术,更没有要求一定要使用HTTP.其目标是为了创建具有良好扩展性的分布式系统. 可用一句话 ...
- Django rest framework 权限操作(源码分析)
知识回顾http://www.cnblogs.com/ctztake/p/8419059.html 这一篇是基于上一篇写的,上一篇谢了认证的具体流程,看懂了上一篇这一篇才能看懂, 当用户访问是 首先执 ...
- Django Rest Framework源码剖析(五)-----解析器
一.简介 解析器顾名思义就是对请求体进行解析.为什么要有解析器?原因很简单,当后台和前端进行交互的时候数据类型不一定都是表单数据或者json,当然也有其他类型的数据格式,比如xml,所以需要解析这类数 ...
- Django Rest Framework源码剖析(二)-----权限
一.简介 在上一篇博客中已经介绍了django rest framework 对于认证的源码流程,以及实现过程,当用户经过认证之后下一步就是涉及到权限的问题.比如订单的业务只能VIP才能查看,所以这时 ...
- Django Rest Framework源码剖析(八)-----视图与路由
一.简介 django rest framework 给我们带来了很多组件,除了认证.权限.序列化...其中一个重要组件就是视图,一般视图是和路由配合使用,这种方式给我们提供了更灵活的使用方法,对于使 ...
- Django Rest Framework源码剖析(七)-----分页
一.简介 分页对于大多数网站来说是必不可少的,那你使用restful架构时候,你可以从后台获取数据,在前端利用利用框架或自定义分页,这是一种解决方案.当然django rest framework提供 ...
随机推荐
- 解决react-native软键盘弹出挡住输入框的问题
解决react-native软键盘弹出挡住输入框的问题 写登录页面,整体界面居中之后就出现软键盘弹出挡住输入框,用户体验不好的情况.用了RN官方的KeyboardAvoidingView组件,会有多出 ...
- GCC栈溢出保护
逆向过elf程序都知道,GCC的canary,x86_64下从fs:0x28偏移处获取,32位下从gs:0x14偏移处获取.但知道canary如何产生,为什么在这里取的人比较少. 下面以x86_64平 ...
- POJ 1200:Crazy Search(哈希)
Crazy Search Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 32483 Accepted: 8947 Des ...
- hdu 1864 最大报销额 01背包
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s) ...
- CodeForces 682A
D - Alyona and Numbers Time Limit:1000MS Memory Limit:262144KB 64bit IO Format:%I64d & %I64u Sub ...
- django 一个关于分组查询的问题分析
ret=Emp.objects.values('dep').annotate(avg_salary=Avg('salary')) print(ret) # ---*******单表分组查询ORM总结: ...
- Caused by: java.sql.SQLException: ORA-24816: 在实际的 LONG 或 LOB 列之后提供了扩展的非 LONG 绑定数据
今天客户说报告草稿保存不了,跟踪错误bug,了解到以下reason: 异常出现的环境:oracle11g + Hibernate 错误分析:这是oracle 11g在clob字段中的一个bug,ora ...
- canvas 使用 isPointInPath() 判断鼠标位置是否在绘制的元素上
canvas 里绘制的图形不是一个实体 DOM,所以要给每个绘制的图形添加事件操作比给 DOM 添加事件要复杂很多. 所以,我们需要使用一个 canvas 的 isPointInPath(x, y) ...
- Go Example--数组
package main import "fmt" func main() { //定义一个数组并完成初始化,初始值为对应的零值 var a [5]int fmt.Println( ...
- linux 之sed
sed 用法 sed [-nefr] [action] -i 直接修改文件内容,而不是像其他命令那样只是输出到终端 a新增c取代d删除i插入p列印常与sed -n 使用s取代 有一点需要注意的是:如果 ...