djangorestframework源码分析

本文环境python3.5.2,djangorestframework (3.5.1)系列

djangorestframework源码分析-generics的执行流程

在使用Django框架的同时,一般会使用djangorestframework第三方库对Django的处理做扩展,以便更易于开发。本文就分析restframework的generics中的view的处理流程,示例代码如下;

 ...# 路由配置('^api/business_application/?$', TestAPI.as_view()),...# 接口函数
from rest_framework.generics import GenericAPIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Responseclass TestAPI(GenericAPIView):permission_classes = (IsAuthenticated,)def post(self, request):return Response({"detail": "ok"})

这里先配置了路由,然后设置了对应的TestAPI处理函数。

generics中的GenericAPIView处理过程

首先查看GenericAPIView类的定义,定义如下;

from django.views.generic import Viewclass GenericAPIView(views.APIView):"""Base class for all other generic views."""# You'll need to either set these attributes,# or override `get_queryset()`/`get_serializer_class()`.# If you are overriding a view method, it is important that you call# `get_queryset()` instead of accessing the `queryset` property directly,# as `queryset` will get evaluated only once, and those results are cached# for all subsequent requests.queryset = Noneserializer_class = None# If you want to use object lookups other than pk, set 'lookup_field'.# For more complex lookup requirements override `get_object()`.lookup_field = 'pk'lookup_url_kwarg = None# The filter backend classes to use for queryset filteringfilter_backends = api_settings.DEFAULT_FILTER_BACKENDS# The style to use for queryset pagination.pagination_class = api_settings.DEFAULT_PAGINATION_CLASS...

该类继承自views.APIView类,继续查看该类,

    class APIView(View):# The following policies may be set at either globally, or per-view.renderer_classes = api_settings.DEFAULT_RENDERER_CLASSESparser_classes = api_settings.DEFAULT_PARSER_CLASSESauthentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSESthrottle_classes = api_settings.DEFAULT_THROTTLE_CLASSESpermission_classes = api_settings.DEFAULT_PERMISSION_CLASSEScontent_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASSmetadata_class = api_settings.DEFAULT_METADATA_CLASSversioning_class = api_settings.DEFAULT_VERSIONING_CLASS# Allow dependency injection of other settings to make testing easier.settings = api_settings# Mark the view as being included or excluded from schema generation.exclude_from_schema = False@classmethoddef as_view(cls, **initkwargs):"""Store the original class on the view function.This allows us to discover information about the view when we do URLreverse 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_evaluationview = super(APIView, cls).as_view(**initkwargs)view.cls = clsview.initkwargs = initkwargs# Note: session based authentication is explicitly CSRF validated,# all other authentication is CSRF exempt.return csrf_exempt(view)

APIView类继承自Django中的View类,所以根据在Django处理流程中的分析可知,在调用TestAPI.as_view()处理的时候就调用APIView类的该方法,处理方法与Django中的View的处理流程相同,当路由匹配上后就调用了self.dispatch方法进行处理,此时就调用了APIView中的该方法;

# 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 = argsself.kwargs = kwargsrequest = self.initialize_request(request, *args, **kwargs)             # 处理requestself.request = request                                                  # 设置requestself.headers = self.default_response_headers  # deprecate?              # 设置头部信息try:self.initial(request, *args, **kwargs)                              # 执行处理的时候的初始化流程如权限等检查# Get the appropriate handler methodif 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                          # 如果没有找到则使用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

通过如上流程可知,权限的检查等处理过程都在self.initial函数中处理,继续查看该函数,

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 requestneg = 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 permittedself.perform_authentication(request)                                    # 检查request.user是否有该属性self.check_permissions(request)                                         # 检查权限类self.check_throttles(request)                                           # 检查是否超出了访问的频率

其中,最重要的就是做了相关的权限的检查,是否满足配置的权限类是否超过了访问频率;

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 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):                # 执行实例的has_permission方法 判断是否有权限执行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())

其中self.permission_classes和self.throttle_classes都是编写接口时配置的,默认为空,如果需要使用并配置则需要进行重写相关方法;

首先分析一下permission类的执行过程;

class BasePermission(object):"""A base class from which all permission classes should inherit."""def has_permission(self, request, view):"""Return `True` if permission is granted, `False` otherwise."""return Truedef has_object_permission(self, request, view, obj):"""Return `True` if permission is granted, `False` otherwise."""return Trueclass IsAuthenticated(BasePermission):"""Allows access only to authenticated users."""def has_permission(self, request, view):return request.user and is_authenticated(request.user)

此时就是执行了IsAuthenticated实例的has_permission方法,判断是否有user属性并且user是否登陆了,如果需要添加自定义权限可以重写该方法就好。

继续查看Throttle类,以AnonRateThrottle类说明;

class BaseThrottle(object):"""Rate throttling of requests."""def allow_request(self, request, view):"""Return `True` if the request should be allowed, `False` otherwise."""raise NotImplementedError('.allow_request() must be overridden')def get_ident(self, request):"""Identify the machine making the request by parsing HTTP_X_FORWARDED_FORif present and number of proxies is > 0. If not use all ofHTTP_X_FORWARDED_FOR if it is available, if not use REMOTE_ADDR."""xff = request.META.get('HTTP_X_FORWARDED_FOR')remote_addr = request.META.get('REMOTE_ADDR')num_proxies = api_settings.NUM_PROXIESif num_proxies is not None:if num_proxies == 0 or xff is None:return remote_addraddrs = xff.split(',')client_addr = addrs[-min(num_proxies, len(addrs))]return client_addr.strip()return ''.join(xff.split()) if xff else remote_addrdef wait(self):"""Optionally, return a recommended number of seconds to wait beforethe next request."""return Noneclass SimpleRateThrottle(BaseThrottle):"""A simple cache implementation, that only requires `.get_cache_key()`to be overridden.The rate (requests / seconds) is set by a `throttle` attribute on the Viewclass.  The attribute is a string of the form 'number_of_requests/period'.Period should be one of: ('s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day')Previous request information used for throttling is stored in the cache."""cache = default_cache                                   timer = time.timecache_format = 'throttle_%(scope)s_%(ident)s'                               # 渲染的缓存值scope = NoneTHROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES                        # 获取配置的频率def __init__(self):if not getattr(self, 'rate', None):                                     # 如果没有改属性self.rate = self.get_rate()                                         # 获取并设置该属性self.num_requests, self.duration = self.parse_rate(self.rate)           # 解析次数,和时间def get_cache_key(self, request, view):"""Should return a unique cache-key which can be used for throttling.Must be overridden.May return `None` if the request should not be throttled."""raise NotImplementedError('.get_cache_key() must be overridden')def get_rate(self):"""Determine the string representation of the allowed request rate."""if not getattr(self, 'scope', None):msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %self.__class__.__name__)raise ImproperlyConfigured(msg)try:return self.THROTTLE_RATES[self.scope]except KeyError:msg = "No default throttle rate set for '%s' scope" % self.scoperaise ImproperlyConfigured(msg)def parse_rate(self, rate):"""Given the request rate string, return a two tuple of:<allowed number of requests>, <period of time in seconds>"""if rate is None:return (None, None)num, period = rate.split('/')                                       # 解析时间num_requests = int(num)                                             # 转换数字duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]      # 获取对应的时间return (num_requests, duration)                                     # 返回def allow_request(self, request, view):"""Implement the check to see if the request should be throttled.On success calls `throttle_success`.On failure calls `throttle_failure`."""if self.rate is None:                                               # 如果没有rate属性直接返回return Trueself.key = self.get_cache_key(request, view)                        # 获取缓存的keyif self.key is None:                                                # 如果没有则返回return Trueself.history = self.cache.get(self.key, [])                         # 获取historyself.now = self.timer()                                             # 获取当前时间# Drop any requests from the history which have now passed the# throttle durationwhile self.history and self.history[-1] <= self.now - self.duration:    # 删除掉已经过期的时间self.history.pop()if len(self.history) >= self.num_requests:                              # 如果长度大于设置的数字则失败return self.throttle_failure()return self.throttle_success()                                          # 否则就是成功def throttle_success(self):"""Inserts the current request's timestamp along with the keyinto the cache."""self.history.insert(0, self.now)                                        # 将当前的时间加入到列表中的第一位self.cache.set(self.key, self.history, self.duration)                   # 设置到缓存中return Truedef throttle_failure(self):"""Called when a request to the API has failed due to throttling."""return Falsedef wait(self):"""Returns the recommended next request time in seconds."""if self.history:remaining_duration = self.duration - (self.now - self.history[-1])      # 计算下次间隔时间else:remaining_duration = self.durationavailable_requests = self.num_requests - len(self.history) + 1if available_requests <= 0:return Nonereturn remaining_duration / float(available_requests)class AnonRateThrottle(SimpleRateThrottle):"""Limits the rate of API calls that may be made by a anonymous users.The IP address of the request will be used as the unique cache key."""scope = 'anon'def get_cache_key(self, request, view):if is_authenticated(request.user):                                          # 如果已经认证则不检查return None  # Only throttle unauthenticated requests.return self.cache_format % {'scope': self.scope,'ident': self.get_ident(request)}

主要就是通过一个列表检查访问时间并通过缓存缓存一个列表来进行判断,检查该列表的长度是否和设置的访问次数的大小来判断是否能够继续访问。

当处理完成后,就会调用view的相应方法去处理该请求,请求完成后就返回给前端。

在rest_framework中GenericAPIView继承自APIView,该类主要就是方便了序列表渲染和分页,是CreateAPIView,ListAPIView和RetrieveUpdateAPIView类的基类。

class GenericAPIView(views.APIView):"""Base class for all other generic views."""# You'll need to either set these attributes,# or override `get_queryset()`/`get_serializer_class()`.# If you are overriding a view method, it is important that you call# `get_queryset()` instead of accessing the `queryset` property directly,# as `queryset` will get evaluated only once, and those results are cached# for all subsequent requests.queryset = None                                                         serializer_class = None                                                         # 序列化类# If you want to use object lookups other than pk, set 'lookup_field'.# For more complex lookup requirements override `get_object()`.lookup_field = 'pk'lookup_url_kwarg = None                                                         # url中的key字段名称# The filter backend classes to use for queryset filteringfilter_backends = api_settings.DEFAULT_FILTER_BACKENDS                          # 配置的过滤模板# The style to use for queryset pagination.pagination_class = api_settings.DEFAULT_PAGINATION_CLASS                        # 分页类def get_queryset(self):"""Get the list of items for this view.This must be an iterable, and may be a queryset.Defaults to using `self.queryset`.This method should always be used rather than accessing `self.queryset`directly, as `self.queryset` gets evaluated only once, and those resultsare cached for all subsequent requests.You may want to override this if you need to provide differentquerysets depending on the incoming request.(Eg. return a list of items that is specific to the user)"""assert self.queryset is not None, ("'%s' should either include a `queryset` attribute, ""or override the `get_queryset()` method."% self.__class__.__name__)queryset = self.querysetif isinstance(queryset, QuerySet):# Ensure queryset is re-evaluated on each request.queryset = queryset.all()return queryset                                                         # 获取配置的querysetdef get_object(self):"""Returns the object the view is displaying.You may want to override this if you need to provide non-standardqueryset lookups.  Eg if objects are referenced using multiplekeyword arguments in the url conf."""queryset = self.filter_queryset(self.get_queryset())# Perform the lookup filtering.lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field           # 通过url获取配置去获取条件assert lookup_url_kwarg in self.kwargs, ('Expected view %s to be called with a URL keyword argument ''named "%s". Fix your URL conf, or set the `.lookup_field` ''attribute on the view correctly.' %(self.__class__.__name__, lookup_url_kwarg))filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}obj = get_object_or_404(queryset, **filter_kwargs)                      # 获取对应Model的实例# May raise a permission deniedself.check_object_permissions(self.request, obj)                        # 检查是否有该实例的权限return obj                                                              # 返回def get_serializer(self, *args, **kwargs):"""Return the serializer instance that should be used for validating anddeserializing input, and for serializing output."""serializer_class = self.get_serializer_class()kwargs['context'] = self.get_serializer_context()return serializer_class(*args, **kwargs)                                # 实例化序列化类def get_serializer_class(self):"""Return the class to use for the serializer.Defaults to using `self.serializer_class`.You may want to override this if you need to provide differentserializations depending on the incoming request.(Eg. admins get full serialization, others get basic serialization)"""assert self.serializer_class is not None, ("'%s' should either include a `serializer_class` attribute, ""or override the `get_serializer_class()` method."% self.__class__.__name__)return self.serializer_class                                                # 获取序列化类def get_serializer_context(self):"""Extra context provided to the serializer class."""return {'request': self.request,'format': self.format_kwarg,'view': self}                                                                           # 获取序列化的contextdef filter_queryset(self, queryset):"""Given a queryset, filter it with whichever filter backend is in use.You are unlikely to want to override this method, although you may needto call it either from a list view, or from a custom `get_object`method if you want to apply the configured filtering backend to thedefault queryset."""for backend in list(self.filter_backends):                                  # 过滤模板,根据条件过滤queryset = backend().filter_queryset(self.request, queryset, self)return queryset@propertydef paginator(self):"""The paginator instance associated with the view, or `None`."""if not hasattr(self, '_paginator'):                                 # 判断是否有_paginator属性if self.pagination_class is None:self._paginator = Noneelse:self._paginator = self.pagination_class()                   # 为空则实例化分页类return self._paginatordef paginate_queryset(self, queryset):"""Return a single page of results, or `None` if pagination is disabled."""if self.paginator is None:return Nonereturn self.paginator.paginate_queryset(queryset, self.request, view=self)def get_paginated_response(self, data):"""Return a paginated style `Response` object for the given output data."""assert self.paginator is not Nonereturn self.paginator.get_paginated_response(data)

该类所有的方法都为后面的RetrieveUpdateAPIView等方法进行了扩展。

总结

本文内容相对简单,大致描述了djangorestframework中的view的处理流程,其中进行了相关的权限检查和访问频率的检查,然后讲述了generics类的基本构成,该类提供的方法都是其他APIView的实现其功能的基础。

djangorestframework源码分析1:generics中的view执行流程相关推荐

  1. React Native 源码分析(三)——Native View创建流程

    1.React Native 源码分析(一)-- 启动流程 2.React Native 源码分析(二)-- 通信机制 3.React Native 源码分析(三)-- Native View创建流程 ...

  2. mysql8.0源代码解析_MySQL8.0.11源码分析之mysql关键函数和执行流程

    mysql是命令行客户端程序 ,交互式输入SQL语句或从文件以批处理模式执行它们的命令行工具. 入口函数 int main(int argc, char *argv[]) { if (get_opti ...

  3. djangorestframework源码分析2:serializer序列化数据的执行流程

    djangorestframework源码分析 本文环境python3.5.2,djangorestframework (3.5.1)系列 djangorestframework源码分析-serial ...

  4. springboot集成mybatis源码分析-mybatis的mapper执行查询时的流程(三)

    springboot集成mybatis源码分析-mybatis的mapper执行查询时的流程(三) 例: package com.example.demo.service;import com.exa ...

  5. Mybatis 源码分析(一)配置文件加载流程

    Mybatis 源码分析(一)配置文件加载流程 1.项目构建 引入依赖 <dependency><groupId>org.mybatis</groupId>< ...

  6. 【Android SDM660源码分析】- 02 - UEFI XBL QcomChargerApp充电流程代码分析

    [Android SDM660源码分析]- 02 - UEFI XBL QcomChargerApp充电流程代码分析 一.加载 UEFI 默认应用程序 1.1 LaunchDefaultBDSApps ...

  7. Linux brk(),mmap()系统调用源码分析3:brk()的内存申请流程

    Linux brk(),mmap()系统调用源码分析 brk()的内存申请流程 荣涛 2021年4月30日 内核版本:linux-5.10.13 注释版代码:https://github.com/Rt ...

  8. SRS流媒体服务器源码分析(一):Rtmp publish流程

    1.线程模型 srs使用了state-threads协程库,是单线程多协程模型. 这个协程的概念类似于lua的协程,都是单线程中可以创建多个协程.而golang中的goroutine协程是多线程并发的 ...

  9. lodash源码分析之compact中的遍历

    小时候, 乡愁是一枚小小的邮票, 我在这头, 母亲在那头. 长大后,乡愁是一张窄窄的船票, 我在这头, 新娘在那头. 后来啊, 乡愁是一方矮矮的坟墓, 我在外头, 母亲在里头. 而现在, 乡愁是一湾浅 ...

最新文章

  1. c语言申请字符串动态,【分享】C语言动态长度字符串
  2. python软件界面-用Html来写Python桌面软件的UI界面-htmlPy
  3. httpservletrequest_了解HttpServletRequest 对象 基本应用
  4. 贝叶斯算法对文本进行分类实例
  5. 朋友圈加粗字体数字_数字+符码:医院数码导视系统畅想起来
  6. sublime text插件emmet自定义模板
  7. C# 序列化之二进制
  8. 陕西师范大学计算机学院课表,陕西师范大学数学和信息科学学院课程表.doc
  9. [T-ARA/筷子兄弟][Little Apple]
  10. 动态库和静态库的区别
  11. 计算机控制plc开机,PLC控制系统与工控计算机控制系统的区别
  12. Network Trimming: 数据指导的神经剪枝方法
  13. c语言设计数独出题目及答案,c语言题目-数独-求大神解释题目意思和分析题目和代码知道...
  14. 谷歌开始卷自己,AI架构Pathways加持,推出200亿生成模型
  15. 「C++小游戏教程」基本技巧(1)——随机化
  16. 小米,苹果,百度,三星等公司的智能语音识别功能如何测试?
  17. 树莓派存储方案_还在用笨重的NAS存储服务器?你可以自己动手用树莓派DIY一个...
  18. win10怎么卸载linux小红帽,win10下使用Linux(ubuntu18.04)
  19. TCP三次握手,四次挥手详解
  20. 二十四、V4L2框架主要结构体分析和虚拟摄像头驱动编写

热门文章

  1. 对比四种爬虫定位元素方法,你更爱哪个?
  2. 958毕业,苦学Java,竟被二本毕业生吊打!网友:确实厉害!
  3. 美团十年,支撑最大规模外卖配送的一站式机器学习平台如何炼成?
  4. 肖仰华:知识图谱构建的三要素、三原则和九大策略 | AI ProCon 2019
  5. 实战 | 如何用最快的速度学会Dlib人脸识别开发?
  6. 深度学习难,这本书让你轻松学深度学习
  7. 终于有人把数据、信息、算法、统计、概率和数据挖掘都讲明白了!
  8. 受用一生的高效PyCharm使用技巧
  9. 4月机器学习热文出炉,这10篇文章你读了吗?
  10. 从FPN到Mask R-CNN,一文告诉你Facebook的计算机视觉有多强