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")
這里我們需要注意一下;
在視圖函數執行之前,我們執行的操作為:
- 版本控制
- 認證
- 權限
- 訪問頻率控制

浙公網安備 33010602011771號