Django rest framework源码分析(4)----版本
新建一个工程Myproject和一个app名为api
(1)api/models.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | from django.db import models class UserInfo(models.Model): USER_TYPE = ( ( 1 , '普通用户' ), ( 2 , 'VIP' ), ( 3 , 'SVIP' ) ) user_type = models.IntegerField(choices = USER_TYPE) username = models.CharField(max_length = 32 ,unique = True ) password = models.CharField(max_length = 64 ) group = models.ForeignKey( 'UserGroup' ,on_delete = models.CASCADE) roles = models.ManyToManyField( 'Role' ) class UserToken(models.Model): user = models.OneToOneField( 'UserInfo' ,on_delete = models.CASCADE) token = models.CharField(max_length = 64 ) class UserGroup(models.Model): title = models.CharField(max_length = 32 ) class Role(models.Model): title = models.CharField(max_length = 32 ) |
(2)Myproject/urls.py
1 2 3 4 5 6 7 | from django.contrib import admin from django.urls import path,include urlpatterns = [ #path('admin/', admin.site.urls), path( 'api/' ,include( 'api.urls' ) ), ] |
(3)api/urls.py
1 2 3 4 5 6 7 8 | # api/urls.py from django.urls import path from .views import UserView urlpatterns = [ path( 'users/' , UserView.as_view()), ] |
(4)views.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # api/views.py from django.shortcuts import render,HttpResponse from rest_framework.views import APIView from rest_framework.request import Request from rest_framework.versioning import QueryParameterVersioning class UserView(APIView): versioning_class = QueryParameterVersioning def get( self ,request, * args, * * kwargs): #获取版本 print (request.version) return HttpResponse( '用户列表' ) |
(5)settings.py
1 2 3 4 5 6 | #版本 REST_FRAMEWORK = { "DEFAULT_VERSION" : 'v1' , #默认的版本 "ALLOWED_VERSIONS" :[ 'v1' , 'v2' ], #允许的版本 "VERSION_PARAM" : 'version' #GET方式url中参数的名字 ?version=xxx } |
1.url中通过GET传参
QueryParameterVersioning用于去GET参数中取version
1 | http: / / 127.0 . 0.1 : 8000 / api / users / ?version = v2 |
后台可以看到当前的版本
如果url中没有传版本参数,则显示默认的版本("DEFAULT_VERSION":'v1')
1 | http: / / 127.0 . 0.1 : 8000 / api / users / |
如果url传的版本超过settings中的允许范围则报错
1 | http: / / 127.0 . 0.1 : 8000 / api / users / ?version = v3 |
2.在URLPATH中获取
(1)修改api/urls.py
通常情况我门应该用URLPATH的方式,而不是用前面GET()传参方式
url里面通过正则表达式定义哪些版本,
1 2 3 4 5 6 7 8 | # api/urls.py from django.urls import path,re_path from .views import UserView urlpatterns = [ re_path( '(?P<version>[v1|v2]+)/users/' , UserView.as_view()), ] |
(2)views.py
URLPathVersioning:去url路径里面获取版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # api/views.py from django.shortcuts import render,HttpResponse from rest_framework.views import APIView from rest_framework.request import Request from rest_framework.versioning import URLPathVersioning class UserView(APIView): versioning_class = URLPathVersioning def get( self ,request, * args, * * kwargs): #获取版本 print (request.version) return HttpResponse( '用户列表' ) |
这个URLPathVersioning我们可以放到settings里面,全局配置,就不用写到views里面,每个类都要写一遍了
settings.py
1 2 3 4 5 6 7 8 9 10 11 12 | # 版本 # REST_FRAMEWORK = { # "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning", # "DEFAULT_VERSION":'v1', #默认的版本 # "ALLOWED_VERSIONS":['v1','v2'], #允许的版本 # "VERSION_PARAM":'version' #get方式url中参数的名字 ?version=xxx # } #全局 REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS" : "rest_framework.versioning.URLPathVersioning" , } |
修改views.py
1 2 3 4 5 6 7 8 9 10 11 12 | # api/views.py from django.shortcuts import render,HttpResponse from rest_framework.views import APIView from rest_framework.request import Request class UserView(APIView): def get( self ,request, * args, * * kwargs): #获取版本 print (request.version) return HttpResponse( '用户列表' ) |
浏览器访问地址
1 | http: / / 127.0 . 0.1 : 8000 / api / v1 / users / |
然后后台拿到版本信息
3.反向解析访问的url
(1)api/urls.py
添加name = 'api_user'
1 2 3 4 5 6 7 8 | # api/urls.py from django.urls import path,re_path from .views import UserView urlpatterns = [ re_path( '(?P<version>[v1|v2]+)/users/' , UserView.as_view(),name = 'api_user' ), ] |
(2)views.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # api/views.py from django.shortcuts import render,HttpResponse from rest_framework.views import APIView from rest_framework.request import Request class UserView(APIView): def get( self ,request, * args, * * kwargs): #获取版本 print (request.version) #获取处理版本的对象 print (request.versioning_scheme) #获取浏览器访问的url,reverse反向解析 #需要两个参数:viewname就是url中的别名,request=request是url中要传入的参数 #(?P<version>[v1|v2]+)/users/,这里本来需要传version的参数,但是version包含在request里面(源码里面可以看到),所有只需要request=request就可以 url_path = request.versioning_scheme.reverse(viewname = 'api_user' ,request = request) print (url_path) # self.dispatch return HttpResponse( '用户列表' ) |
浏览器访问
1 | http: / / 127.0 . 0.1 : 8000 / api / v1 / users / |
后台获取
源码流程
(1)dispatch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 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 = args self .kwargs = kwargs #对原始request进行加工,丰富了一些功能 #Request( # request, # parsers=self.get_parsers(), # authenticators=self.get_authenticators(), # negotiator=self.get_content_negotiator(), # parser_context=parser_context # ) #request(原始request,[BasicAuthentications对象,]) #获取原生request,request._request #获取认证类的对象,request.authticators #1.封装request request = self .initialize_request(request, * args, * * kwargs) self .request = request self .headers = self .default_response_headers # deprecate? try : #2.认证 self .initial(request, * args, * * kwargs) # Get the appropriate handler method 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 |
(2)initial
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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 request neg = self .perform_content_negotiation(request) request.accepted_renderer, request.accepted_media_type = neg # Determine the API version, if versioning is in use. #request.version获取版本信息 #request.versioning_scheme获取处理版本你的对象 version, scheme = self .determine_version(request, * args, * * kwargs) request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted #4.实现认证 self .perform_authentication(request) #5.权限判断 self .check_permissions(request) #6.控制访问频率 self .check_throttles(request) |
(3)determine_version
1 2 3 4 5 6 7 8 9 | def determine_version( self , request, * args, * * kwargs): """ If versioning is being used, then determine any API version for the incoming request. Returns a two-tuple of (version, versioning_scheme) """ if self .versioning_class is None : return ( None , None ) scheme = self .versioning_class() return (scheme.determine_version(request, * args, * * kwargs), scheme) |
(4)versioning_class
URLPathVersioning源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | class URLPathVersioning(BaseVersioning): """ To the client this is the same style as `NamespaceVersioning`. The difference is in the backend - this implementation uses Django's URL keyword arguments to determine the version. 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 """ 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 def reverse( self , viewname, args = None , kwargs = None , request = None , format = None , * * extra): if request.version is not None : kwargs = {} if (kwargs is None ) else kwargs kwargs[ self .version_param] = request.version return super (URLPathVersioning, self ).reverse( viewname, args, kwargs, request, format , * * extra ) |
可以看到
(1)url配置
(2)determine_version
里面有个is_allowed_version,点进去可以看到一些基本参数 (继承BaseVersioning基类)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class BaseVersioning( object ): #默认的版本 default_version = api_settings.DEFAULT_VERSION #允许的版本 allowed_versions = api_settings.ALLOWED_VERSIONS #默认参数(是version,比如你可以自定义为v) version_param = api_settings.VERSION_PARAM def determine_version( self , request, * args, * * kwargs): msg = '{cls}.determine_version() must be implemented.' raise NotImplementedError(msg. format ( cls = self .__class__.__name__ )) def reverse( self , viewname, args = None , kwargs = None , request = None , format = None , * * extra): return _reverse(viewname, args, kwargs, request, format , * * extra) def is_allowed_version( self , version): if not self .allowed_versions: return True return ((version is not None and version = = self .default_version) or (version in self .allowed_versions)) |
生活不会突变,你要做的只是耐心和积累。人这一辈子没法做太多的事情,所以每一件都要做得精彩绝伦。你的时间有限,做喜欢的事情会令人愉悦,所以跟随自己的本心。
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步