Django Rest Framework源码剖析(四)-----API版本
<table style="height: 30px; background-color: #afeeee; width: 1266px; ; width: 1266px;" border="0"> |
1.在url中传递版本:如http://www.example.com/api?version=v1
和其他组建一样,我们在utils里面建立version.py,添加版本类
<span style="color: #0000ff;">def determine_version(self,request,*args,**<span style="color: #000000;">kwargs):
myversion=request.query_params.get(<span style="color: #800000;">'<span style="color: #800000;">version<span style="color: #800000;">'<span style="color: #000000;">)
<span style="color: #0000ff;">return myversion
在订单视图中应用版本,(当然直接可以使用request.get获取)
models.py
user_type_choice =<span style="color: #000000;"> (
(1,<span style="color: #800000;">"<span style="color: #800000;">普通用户<span style="color: #800000;">"<span style="color: #000000;">),(2,<span style="color: #800000;">"<span style="color: #800000;">会员<span style="color: #800000;">"<span style="color: #000000;">),)
user_type = models.IntegerField(choices=<span style="color: #000000;">user_type_choice)
username = models.CharField(max_length=32,unique=<span style="color: #000000;">True)
password = models.CharField(max_length=64<span style="color: #000000;">)
<span style="color: #0000ff;">class<span style="color: #000000;"> UserToken(models.Model):
user = models.OneToOneField(to=<span style="color: #000000;">UserInfo)
token = models.CharField(max_length=64)
urls.py
url(r</span><span style="color: #800000;">'</span><span style="color: #800000;">^api/v1/auth</span><span style="color: #800000;">'</span><span style="color: #000000;">,views.AuthView.as_view()),url(r</span><span style="color: #800000;">'</span><span style="color: #800000;">^api/v1/order</span><span style="color: #800000;">'</span><span style="color: #000000;">,views.OrderView.as_view()),]</span></pre>
views.py
<span style="color: #800000;">"""<span style="color: #800000;">
认证类
<span style="color: #800000;">"""
<span style="color: #0000ff;">def</span><span style="color: #000000;"> authenticate(self,request):
token </span>= request._request.GET.get(<span style="color: #800000;">"</span><span style="color: #800000;">token</span><span style="color: #800000;">"</span><span style="color: #000000;">)
toke_obj </span>= models.UserToken.objects.filter(token=<span style="color: #000000;">token).first()
</span><span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> toke_obj:
</span><span style="color: #0000ff;">raise</span> exceptions.AuthenticationFailed(<span style="color: #800000;">"</span><span style="color: #800000;">用户认证失败</span><span style="color: #800000;">"</span><span style="color: #000000;">)
</span><span style="color: #0000ff;">return</span> (toke_obj.user,toke_obj) <span style="color: #008000;">#</span><span style="color: #008000;"> 这里返回值一次给request.user,request.auth</span>
<span style="color: #0000ff;">def</span><span style="color: #000000;"> authenticate_header(self,val):
</span><span style="color: #0000ff;">pass</span>
<span style="color: #0000ff;">def
<span style="color: #000000;"> md5(user):ctime =<span style="color: #000000;"> str(time.time())
m = hashlib.md5(bytes(user,encoding=<span style="color: #800000;">"<span style="color: #800000;">utf-8<span style="color: #800000;">"<span style="color: #000000;">))
m.update(bytes(ctime,encoding=<span style="color: #800000;">"<span style="color: #800000;">utf-8<span style="color: #800000;">"<span style="color: #000000;">))
<span style="color: #0000ff;">return<span style="color: #000000;"> m.hexdigest() <span style="color: #0000ff;">class<span style="color: #000000;"> AuthView(APIView):
<span style="color: #800000;">"""<span style="color: #800000;">登陆认证<span style="color: #800000;">"""
<span style="color: #0000ff;">def dispatch(self,<span style="color: #000000;">kwargs):
<span style="color: #0000ff;">return super(AuthView,self).dispatch(request,<span style="color: #000000;">kwargs)
</span><span style="color: #0000ff;">def</span> get(self,**<span style="color: #000000;">kwargs):
</span><span style="color: #0000ff;">return</span> HttpResponse(<span style="color: #800000;">'</span><span style="color: #800000;">get</span><span style="color: #800000;">'</span><span style="color: #000000;">)
</span><span style="color: #0000ff;">def</span> post(self,**<span style="color: #000000;">kwargs):
ret </span>= {<span style="color: #800000;">'</span><span style="color: #800000;">code</span><span style="color: #800000;">'</span>: 1000,<span style="color: #800000;">'</span><span style="color: #800000;">msg</span><span style="color: #800000;">'</span>: <span style="color: #800000;">"</span><span style="color: #800000;">登录成功</span><span style="color: #800000;">"</span><span style="color: #000000;">}
</span><span style="color: #0000ff;">try</span><span style="color: #000000;">:
user </span>= request._request.POST.get(<span style="color: #800000;">"</span><span style="color: #800000;">username</span><span style="color: #800000;">"</span><span style="color: #000000;">)
pwd </span>= request._request.POST.get(<span style="color: #800000;">"</span><span style="color: #800000;">password</span><span style="color: #800000;">"</span><span style="color: #000000;">)
obj </span>= models.UserInfo.objects.filter(username=user,password=<span style="color: #000000;">pwd).first()
</span><span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> obj:
ret[</span><span style="color: #800000;">'</span><span style="color: #800000;">code</span><span style="color: #800000;">'</span>] = 1001<span style="color: #000000;">
ret[</span><span style="color: #800000;">'</span><span style="color: #800000;">msg</span><span style="color: #800000;">'</span>] = <span style="color: #800000;">"</span><span style="color: #800000;">用户名或密码错误</span><span style="color: #800000;">"</span>
<span style="color: #0000ff;">else</span><span style="color: #000000;">:
token </span>=<span style="color: #000000;"> md5(user)
models.UserToken.objects.update_or_create(user</span>=obj,defaults={<span style="color: #800000;">"</span><span style="color: #800000;">token</span><span style="color: #800000;">"</span><span style="color: #000000;">: token})
ret[</span><span style="color: #800000;">'</span><span style="color: #800000;">token</span><span style="color: #800000;">'</span>] =<span style="color: #000000;"> token
</span><span style="color: #0000ff;">except</span><span style="color: #000000;"> Exception as e:
ret[</span><span style="color: #800000;">'</span><span style="color: #800000;">code</span><span style="color: #800000;">'</span>] = 1002<span style="color: #000000;">
ret[</span><span style="color: #800000;">'</span><span style="color: #800000;">msg</span><span style="color: #800000;">'</span>] = <span style="color: #800000;">"</span><span style="color: #800000;">请求异常</span><span style="color: #800000;">"</span>
<span style="color: #0000ff;">return</span><span style="color: #000000;"> JsonResponse(ret)
<span style="color: #0000ff;">class<span style="color: #000000;"> OrderView(APIView):
<span style="color: #800000;">'''<span style="color: #800000;">查看订单<span style="color: #800000;">'''
<span style="color: #0000ff;">from utils.permissions <span style="color: #0000ff;">import<span style="color: #000000;"> MyPremission
<span style="color: #0000ff;">from utils.version <span style="color: #0000ff;">import<span style="color: #000000;"> Myversion
authentication_classes = [Authentication,] <span style="color: #008000;">#<span style="color: #008000;">添加权限控制
versioning_class =<span style="color: #000000;"> Myversion
<span style="color: #0000ff;">def get(self,**<span style="color: #000000;">kwargs):
<span style="color: #0000ff;">print<span style="color: #000000;">(request.version)
ret </span>= {<span style="color: #800000;">'</span><span style="color: #800000;">code</span><span style="color: #800000;">'</span>:1000,safe=True)</pre>
使用postman发送请求:http://127.0.0.1:8000/api/v1/order?token=7c191332ba452abefe516ff95ea9994a&version=v1,后台可获取版本。
当然上面获取版本方式还有更为简单的获取版本方法,使用QueryParameterVersioning,其就是封装的以上过程。
当然,DRF也提供了可配置的版本,并且还能控制版本使用
settings.py
使用postman验证,发送带token和版本http://127.0.0.1:8000/api/v1/order?token=7c191332ba452abefe516ff95ea9994a&version=v3
结果:
可见版本配置生效。
2.使用url路径传递版本,如http://www.example.com/api/v1,django rest framework 当然也为我们提供了类:URLPathVersioning
为了区分,这里新建url和view,如下:
urls.py
url(r</span><span style="color: #800000;">'</span><span style="color: #800000;">^api/v1/auth</span><span style="color: #800000;">'</span><span style="color: #000000;">,<span style="color: #ff6600;"> url(r</span></span><span style="color: #ff6600;">'^api/(?P<version>[v1|v2]+)/user'</span><span style="color: #000000;"><span style="color: #ff6600;">,views.UserView.as_view()),# 新建的url</span>
]
UserView
<span style="color: #0000ff;">from</span> rest_framework.versioning <span style="color: #0000ff;">import</span><span style="color: #000000;"><span style="color: #ff6600;"> URLPathVersioning</span>
versioning_class </span>=<span style="color: #000000;">URLPathVersioning
</span><span style="color: #0000ff;">def</span> get(self,**<span style="color: #000000;">kwargs):
</span><span style="color: #0000ff;">print</span><span style="color: #000000;">(request.version) <span style="color: #ff6600;">#获取版本</span>
res</span>={<span style="color: #800000;">"</span><span style="color: #800000;">name</span><span style="color: #800000;">"</span>:<span style="color: #800000;">"</span><span style="color: #800000;">wd</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">age</span><span style="color: #800000;">"</span>:22<span style="color: #000000;">}
</span><span style="color: #0000ff;">return</span> JsonResponse(res,safe=True)</pre>
使用postman请求:http://127.0.0.1:8000/api/v1/user,同样后台能拿到版本结果。
1.APIView类的dispatch源码:
<span style="color: #0000ff;">try</span><span style="color: #000000;">:
self.initial(request,</span>*args,**<span style="color: #000000;">kwargs)
</span><span style="color: #008000;">#</span><span style="color: #008000;"> Get the appropriate handler method</span>
<span style="color: #0000ff;">if</span> request.method.lower() <span style="color: #0000ff;">in</span><span style="color: #000000;"> self.http_method_names:
handler </span>=<span style="color: #000000;"> getattr(self,request.method.lower(),self.http_method_not_allowed)
</span><span style="color: #0000ff;">else</span><span style="color: #000000;">:
handler </span>=<span style="color: #000000;"> self.http_method_not_allowed
response </span>= handler(request,**<span style="color: #000000;">kwargs)
</span><span style="color: #0000ff;">except</span><span style="color: #000000;"> Exception as exc:
response </span>=<span style="color: #000000;"> self.handle_exception(exc)
self.response </span>= self.finalize_response(request,response,**<span style="color: #000000;">kwargs)
</span><span style="color: #0000ff;">return</span> self.response</pre>
2.接着执行self.inital方法:
</span><span style="color: #008000;">#</span><span style="color: #008000;"> Perform content negotiation and store the accepted info on the request</span>
neg =<span style="color: #000000;"> self.perform_content_negotiation(request)
request.accepted_renderer,request.accepted_media_type </span>=<span style="color: #000000;"> neg
</span><span style="color: #008000;">#</span><span style="color: #008000;"> Determine the API version,if versioning is in use.</span>
<span style="color: #008000;">#</span><span style="color: #008000;">###版本控制</span>
version,scheme = self.determine_version(request,**<span style="color: #000000;">kwargs)
request.version,request.versioning_scheme </span>=<span style="color: #000000;"> version,scheme
</span><span style="color: #008000;">#</span><span style="color: #008000;"> Ensure that the incoming request is permitted</span>
<span style="color: #008000;">#</span><span style="color: #008000;">2.实现认证</span>
<span style="color: #000000;"> self.perform_authentication(request)
<span style="color: #000000;"> self.check_permissions(request)
<span style="color: #008000;">#<span style="color: #008000;">4.频率限制
self.check_throttles(request)
3.可以看到版本控制是在认证之前,首先下执行version,**kwargs),以下是self.determine_version源码:
4.承接?self.determine_version方法执行完成以后,接着执行request.version,request.versioning_scheme = version,scheme,这个不用多说,无非将版本号赋值给request.version属性,版本类对象赋值给request.versioning_scheme,这也就是我们为什么能通过request.version获取版本号的原因。
5.同认证源码一样,self.determine_version方法中使用的版本类self.versioning_class(),在全局中也有配置
</span><span style="color: #008000;">#</span><span style="color: #008000;"> The following policies may be set at either globally,or per-view.</span>
renderer_classes =<span style="color: #000000;"> api_settings.DEFAULT_RENDERER_CLASSES
parser_classes </span>=<span style="color: #000000;"> api_settings.DEFAULT_PARSER_CLASSES
authentication_classes </span>=<span style="color: #000000;"> api_settings.DEFAULT_AUTHENTICATION_CLASSES
throttle_classes </span>=<span style="color: #000000;"> api_settings.DEFAULT_THROTTLE_CLASSES
permission_classes </span>=<span style="color: #000000;"> api_settings.DEFAULT_PERMISSION_CLASSES
content_negotiation_class </span>=<span style="color: #000000;"> api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
metadata_class </span>=<span style="color: #000000;"> api_settings.DEFAULT_METADATA_CLASS
<span style="color: #ff6600;">versioning_class </span></span><span style="color: #ff6600;">= api_settings.DEFAULT_VERSIONING_CLASS #版本处理类配置</span></pre>
6.基于以上源码分析完成以后,下面我们来剖析下,我们示例中所使用的两个版本处理类,具体分析请看注解:
QueryParameterVersioning(BaseVersioning)
</span></span><span style="color: #0000ff;">def</span> determine_version(self,**<span style="color: #000000;">kwargs): ## 获取版本方法
version </span>=<span style="color: #000000;"> request.query_params.get(self.version_param,self.default_version) <span style="color: #ff6600;"># 通过request.query_paras方法获取(本质request.MATE.get)</span>,<span style="color: #ff6600;">default_version默认是version,是在settings中配置的
</span></span><span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> self.is_allowed_version(version): <span style="color: #ff6600;"> #不允许的版本抛出异常
</span></span><span style="color: #0000ff;">raise</span><span style="color: #000000;"> exceptions.NotFound(self.invalid_version_message)
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> version <span style="color: #ff6600;">#无异常则返回版本号
</span></span><span style="color: #0000ff;">def</span> reverse(self,viewname,args=None,kwargs=None,request=None,format=None,**<span style="color: #000000;">extra): <span style="color: #ff6600;">#url 反解析,可以通过该方法生成请求的url,后面会有示例</span>
url </span>=<span style="color: #000000;"> super(QueryParameterVersioning,self).reverse(
viewname,args,kwargs,format,</span>**<span style="color: #000000;">extra
)
</span><span style="color: #0000ff;">if</span> request.version <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> None:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> replace_query_param(url,self.version_param,request.version)
</span><span style="color: #0000ff;">return</span> url</pre>
URLPathVersioning
An example URL conf for two views that accept two different versions.
urlpatterns = [
url(r'^(?P<version>[v1|v2]+)/users/$',users_list,name='users-list'),url(r'^(?P<version>[v1|v2]+)/users/(?P<pk>[0-9]+)/$',users_detail,name='users-detail')
]
GET /1.0/something/ HTTP/1.1
Host: example.com
Accept: application/json
</span><span style="color: #800000;">"""</span><span style="color: #000000;">
invalid_version_message </span>= _(<span style="color: #800000;">'</span><span style="color: #800000;">Invalid version in URL path.</span><span style="color: #800000;">'</span><span style="color: #000000;">) <span style="color: #ff6600;"># 不允许的版本信息,可定制
</span></span><span style="color: #0000ff;">def</span> determine_version(self,**<span style="color: #000000;">kwargs): <span style="color: #ff6600;">## 同样实现determine_version方法获取版本</span>
version </span>=<span style="color: #000000;"> kwargs.get(self.version_param,self.default_version) <span style="color: #ff6600;"># 由于传递的版本在url的正则中,所以从kwargs中获取,self.version_param默认是version
</span></span><span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> self.is_allowed_version(version):
</span><span style="color: #0000ff;">raise</span><span style="color: #000000;"> exceptions.NotFound(self.invalid_version_message) <span style="color: #ff6600;"># 没获取到,抛出异常
</span></span><span style="color: #0000ff;">return</span><span style="color: #000000;"> version <span style="color: #ff6600;"># 正常获取,返回版本号
</span></span><span style="color: #0000ff;">def</span> reverse(self,**<span style="color: #000000;">extra): <span style="color: #ff6600;"># url反解析,后面会有示例
</span></span><span style="color: #0000ff;">if</span> request.version <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> None:
kwargs </span>= {} <span style="color: #0000ff;">if</span> (kwargs <span style="color: #0000ff;">is</span> None) <span style="color: #0000ff;">else</span><span style="color: #000000;"> kwargs
kwargs[self.version_param] </span>=<span style="color: #000000;"> request.version
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> super(URLPathVersioning,</span>**extra</pre>
这个版本类都继承了BaseVersioning:
</span></span><span style="color: #0000ff;">def</span> determine_version(self,**<span style="color: #000000;">kwargs):
msg </span>= <span style="color: #800000;">'</span><span style="color: #800000;">{cls}.determine_version() must be implemented.</span><span style="color: #800000;">'</span>
<span style="color: #0000ff;">raise</span><span style="color: #000000;"> NotImplementedError(msg.format(
cls</span>=self.<span style="color: #800080;">__class__</span>.<span style="color: #800080;">__name__</span><span style="color: #000000;">
))
</span><span style="color: #0000ff;">def</span> reverse(self,**<span style="color: #000000;">extra):
</span><span style="color: #0000ff;">return</span> _reverse(viewname,**<span style="color: #000000;">extra)
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> is_allowed_version(self,version):
</span><span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> self.allowed_versions:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> True
</span><span style="color: #0000ff;">return</span> ((version <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">not</span> None <span style="color: #0000ff;">and</span> version == self.default_version) <span style="color: #0000ff;">or</span><span style="color: #000000;">
(version </span><span style="color: #0000ff;">in</span> self.allowed_versions))</pre>
1.配置url,为view取别名
url(r</span><span style="color: #800000;">'</span><span style="color: #800000;">^api/v1/auth</span><span style="color: #800000;">'</span><span style="color: #000000;">,<span style="color: #ff6600;"> url(r</span></span><span style="color: #ff6600;">'^api/(?P<version>[v1|v2]+)/user',views.UserView.as_view(),name="user_view"</span><span style="color: #000000;"><span style="color: #ff6600;">),</span>
]
2.利用reverse方法反向生成请求的url,UserView视图。
<span style="color: #0000ff;">from</span> rest_framework.versioning <span style="color: #0000ff;">import</span><span style="color: #000000;"> URLPathVersioning
versioning_class </span>=<span style="color: #000000;">URLPathVersioning
</span><span style="color: #0000ff;">def</span> get(self,**<span style="color: #000000;">kwargs):
</span><span style="color: #0000ff;">print</span><span style="color: #000000;">(request.version)
url </span>= request.versioning_scheme.reverse(viewname=<span style="color: #800000;">'</span><span style="color: #800000;">user_view</span><span style="color: #800000;">'</span>,request=<span style="color: #000000;">request)
</span><span style="color: #008000;">#</span><span style="color: #008000;">versioning_scheme已经在源码中分析过了,就是版本类实例化的对象</span>
<span style="color: #0000ff;">print</span><span style="color: #000000;">(url)
res</span>={<span style="color: #800000;">"</span><span style="color: #800000;">name</span><span style="color: #800000;">"</span>:<span style="color: #800000;">"</span><span style="color: #800000;">wd</span><span style="color: #800000;">"</span>,safe=True)</pre>
使用postman发请求:http://127.0.0.1:8000/api/v1/user查看结果如下:
具体配置:
versioning_class =URLPathVersioning
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!