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")
这里我们需要注意一下;
在视图函数执行之前,我们执行的操作为:
- 版本控制
- 认证
- 权限
- 访问频率控制