django restframework框架一

@(python之路)[django restframework框架初识]
django restframework框架初识

1.restframework框架

django-rest-framework框架,基于django的REST框架,是一个强大灵活的构建Web API的工具包。
环境准备:

python3.6 
pip install django==1.11.7
pip install djangorestframework

2.基于django CBV实现接口

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    # url(r'^admin/', admin.site.urls),
    url(r'^user/', views.UserView.as_view()),
]

views.py

from django.shortcuts import render,HttpResponse
from django.views import View

class UserView(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")

这样的化我们就可以通过发送不同的请求来,根据不同请求做出 不同响应。

2.1 源码刨析

问题:为什么他会根据不同请求做出响应?
我们需要查看as_view都做了什么,思路。我们首先先要找到as_view的父类,也就是View.

from django.views import View
class UserView(View):
	pass

分析源码:

@classonlymethod
   def as_view(cls, **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


       update_wrapper(view, cls, updated=())


       update_wrapper(view, cls.dispatch, assigned=())
       return view

我们整体来看,这就是一个闭包

def as_view():
	def view():
		pass
	return view

所以,我们先分析源码中的view()

def view(request, *args, **kwargs):
		  # 这里的self=UserView()
          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
          # self=UserView()
          # self.dispath    这里dispath返回什么用户就看到什么。
          return self.dispatch(request, *args, **kwargs)
      view.view_class = cls
      view.view_initkwargs = initkwargs
      update_wrapper(view, cls, updated=())
      update_wrapper(view, cls.dispatch, assigned=())
      return view

dispath详解

 http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']


def dispatch(self, request, *args, **kwargs):
	# 他会看你请求的方法是不是在http_method_names里边。
	# 根据在不在里边去做出相应的相应。
    if request.method.lower() in self.http_method_names:
	    # self=UserView
	    # 他会执行get/post/put/delete/方法,
	    # getattr找到UserView中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)

3django restframwork小试牛刀

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    # url(r'^admin/', admin.site.urls),
    url(r'^user/', views.UserView.as_view()),
]

views.py

from rest_framework.views import APIView

class UserView(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")

源码刨析

这里是继承了APIview。我们需要从APIview中寻找as_view,查看他是如何工作的。

def as_view(cls, **initkwargs):
      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

      return csrf_exempt(view)

 我们首先来看一下view = super(APIView, cls).as_view(**initkwargs)这条语句。补充:super会根据mro表的继承顺序查找方法。
 他会查找原来的as_view方法。原来的as_view返回的是原来的内部的view函数。
 往下走,我们发现return csrf_exempt(view),这里我们可以认为,他注释了django的csrf_token。所以,rest framework不用csrf_token认证。其他事没有做,还是正常走。找到view在找到dispatch。我们发现父类APIview有自己的dispatch。
dispatch分析

def dispatch(self, request, *args, **kwargs):
	 # 这里的request还是还是原来的request
	 # arg传递的参数
     self.args = args
     self.kwargs = kwargs
     # 将request替换,这里的替换是指将request加了一层封装;下面将详细说明,
     # 1. 封装视图函数
     request = self.initialize_request(request, *args, **kwargs)
     self.request = request
     self.headers = self.default_response_headers  # deprecate?

     try:
	     # 这里是视图函数触发之前就做的操作。
	     # 2. 在视图函数值前
         self.initial(request, *args, **kwargs)
		 # 3. 执行视图函数
         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包括:

  • _request = 老的request(以后要用的化就需要.出来)
  • authenticators = 认证的对象列表

注意:这里的self.request是新的request
response = handler(request, *args, **kwargs)此处做了一个反射,找到视图函数。
2.self.initial

def initial(self, request, *args, **kwargs):
    self.format_kwarg = self.get_format_suffix(**kwargs)
    neg = self.perform_content_negotiation(request)
    request.accepted_renderer, request.accepted_media_type = neg
    # 2.1 处理版本信息
    version, scheme = self.determine_version(request, *args, **kwargs)
    request.version, request.versioning_scheme = version, scheme
    self.perform_authentication(request)
    self.check_permissions(request)
    self.check_throttles(request)

version, scheme = self.determine_version(request, *args, **kwargs)这里边就是处理版本相关的信息的。

def determine_version(self, request, *args, **kwargs):
    if self.versioning_class is None:
        return (None, None)
    # 实例化处理版本的对象,obj = URLPathVersioning()
    scheme = self.versioning_class()
    # 返回的结果
	    # - 方法取得东西obj.determine_version()-->帮助我们拿版本的。
	    # - 对象本身;obj
    return (scheme.determine_version(request, *args, **kwargs), scheme)

如果我们在View.py中UserView中写versioning_class = ‘ ’他会优先执行这里的
版本具体操纵 请看4.1版本

1.request处理:
我们可以先看一下,request是怎么处理的。

 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__)
     )

     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,)

initialize_request:这里将request做了封装。详解一下。

def initialize_request(self, request, *args, **kwargs):

      parser_context = self.get_parser_context(request)
	  # 这的request包括认证对像,
      return Request(
          request,
          parsers=self.get_parsers(),
          # 我们先看这里
          # 这里的 authenticators就是一个一个列表,里边放的是我们自己写的类的
          #一个一个对象。并且把对象封装到request中了。
          authenticators=self.get_authenticators(),
          negotiator=self.get_content_negotiator(),
          parser_context=parser_context
      )

 这里的 authenticators就是一个一个列表,里边放的是我们自己写的类的一个一个对象。并且把对象封装到request中了。这就是认证相关的对象。
get_authenticators:详解
 这是一个列表生成式;

    def get_authenticators(self):
        return [auth() for auth in self.authentication_classes]

我们再看一下,authentication_classes

authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES

authentication_classe默认取配置文件里读.如果我们在自己定义的类class UserView:pass中设置,authentication_classes=[ ],那么它的优先级会比他自己定义的要高。

我们在views.py中看一下request。

class UserView(APIView):


    def get(self,request,*args,**kwargs):
        print(request)
        print(request._request)
        print(request.authenticators)
############### 打印 #####################
<rest_framework.request.Request object at 0x06D20BD0>
<WSGIRequest: GET '/user/'>
[<rest_framework.authentication.SessionAuthentication object at 0x07255670>, <rest_framework.authentication.BasicAuthentication object at 0x07255690>]

 我们可以发现,reqeust是rest_framework做好封装的;而request._request是原来的request.request.authenticators是我们传递过去的几类个对象。

4.rest framework组件

4.1 版本

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    # url(r'^admin/', admin.site.urls),
    url(r'^(?P<version>\w+)/user/', views.UserView.as_view()),
]

views.py

from rest_framework.versioning import URLPathVersioning

class UserView(APIView):
    versioning_class = URLPathVersioning

    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")

我们需要在user前边加上东西
 如果是versioning_class = URLPathVersioning我们来看一下他都做了什么

class URLPathVersioning(BaseVersioning):
	  invalid_version_message = _('Invalid version in URL path.')
	
	  def determine_version(self, request, *args, **kwargs):
	      version = kwargs.get(self.version_param, self.default_version)
	      if not self.is_allowed_version(version):
	          raise exceptions.NotFound(self.invalid_version_message)
	      return version

version_param

class BaseVersioning(object):
    default_version = api_settings.DEFAULT_VERSION
    allowed_versions = api_settings.ALLOWED_VERSIONS
    version_param = api_settings.VERSION_PARAM

当我们打印的时候他就会打印版本信息

class UserView(APIView):
    versioning_class = URLPathVersioning

    def get(self,request,*args,**kwargs):
        print(request.version)
        return HttpResponse("get")
  ############# 打印 ##############
v1

方式二
urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [

    url(r'^user/', views.UserView.as_view()),
]

views.py

from rest_framework.versioning import URLPathVersioning,QueryParameterVersioning

class UserView(APIView):
	# 访问方式http://127.0.0.1:8000/user/?version=v2
    versioning_class = QueryParameterVersioning
    def get(self,request,*args,**kwargs):
        print(request.version)
		return HttpResponse("get")
################# 打印 ########################
v2

QueryParameterVersioning

class QueryParameterVersioning(BaseVersioning):
    invalid_version_message = _('Invalid version in query parameter.')

    def determine_version(self, request, *args, **kwargs):
        # 版本信息是在query_params中拿到的
        # query_params = self._request.GET
        #def query_params(self):
	    #  return self._request.GET
        
        version = request.query_params.get(self.version_param, self.default_version)
        if not self.is_allowed_version(version):
            raise exceptions.NotFound(self.invalid_version_message)
        return version

注意推荐用第一种方式:URLPathVersioning

重点

&emsp:我们所用的都是rest_framework默认写好的配置文件我们需要自己定义一下。我们首先找到然后它的配置文件命,我们在重新定义。

class APIView(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

一步、urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^(?P<version>\w+)/user/', views.UserView.as_view()),
]

所以我们需要在配置settings.py最下边添加
二步、settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config',
    'rest_framework',
]



REST_FRAMEWORK = {
  "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
    "ALLOWED_VERSIONS":["v1","v2"],
    "DEFAULT_VERSION":"v1"

}
# rest_framework.versioning.URLPathVersioning 是URLPathVersioning 模块调用的路径
# ALLOWED_VERSIONS 限制版本号,只能访问v1和v2两个版本号
# DEFAULT_VERSION 如果没有取到,默认取v1

ALLOWED_VERSIONS 由来


class URLPathVersioning(BaseVersioning):
    invalid_version_message = _('Invalid version in URL path.')
    def determine_version(self, request, *args, **kwargs):
        version = kwargs.get(self.version_param, self.default_version)
        if not self.is_allowed_version(version):
            raise exceptions.NotFound(self.invalid_version_message)
        return version
#####################################################################
version_param
class BaseVersioning(object):
    default_version = api_settings.DEFAULT_VERSION
    allowed_versions = api_settings.ALLOWED_VERSIONS
    version_param = api_settings.VERSION_PARAM

 当然版本也可以放在视图局部,根据需求设置;版本可以有好多中可以是请求头,url;子域名中;这里推荐用我们实现的方式之中。

三步、views.py

from rest_framework.versioning import URLPathVersioning

class UserView(APIView):

    # versioning_class = URLPathVersioning   如果在这里添加,优先级会高于全局

    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")

这里我们需要注意一下;
在视图函数执行之前,我们执行的操作为:

  • 版本控制
  • 认证
  • 权限
  • 访问频率控制
posted @ 2018-05-30 17:19  zz小公子  阅读(824)  评论(0编辑  收藏  举报