Version 1.
1. Create a new project Myproject and an app named api
(1)api/models.py
from django.db import models class UserInfo(models.Model): USER_TYPE = ( (1,'Ordinary users'), (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
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
# api/urls.py from django.urls import path from .views import UserView urlpatterns = [ path('users/', UserView.as_view()), ]
(4)views.py
# 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): #Get version print(request.version) return HttpResponse('User list')
(5)settings.py
#Edition REST_FRAMEWORK = { "DEFAULT_VERSION":'v1', #Default version "ALLOWED_VERSIONS":['v1','v2'], #Allowed versions "VERSION_PARAM":'version' #Name of parameter in GET method url? version=xxx }
2. GET parameter in URL
QueryParameterVersioning is used to GET the version in the GET parameter
http://127.0.0.1:8000/api/users/?version=v2
The current version can be seen in the background
If the version passed by the url exceeds the allowed range in settings, an error will be reported
http://127.0.0.1:8000/api/users/?version=v3
2, Get in URLPATH
(1) Modify api/urls.py
In general, our door should use URLPATH instead of GET() to pass parameters
Which versions are defined by regular expressions in the url,
# 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()), ]
Modify views.py
URLPathVersioning: get the version in the url path
# 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): #Get version print(request.version) return HttpResponse('User list')
This URLPathVersioning can be put into settings, global configuration, instead of being written into views. Each class needs to be written once
settings.py
# Edition # REST_FRAMEWORK = { # "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning", # "DEFAULT_VERSION":'v1', #Default version # "ALLOWED_VERSIONS":['v1','v2'], #Allowed versions # "VERSION_PARAM":'version' #Name of parameter in get method url? version=xxx # } #Overall situation REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning", }
Modify views.py
# 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): #Get version print(request.version) return HttpResponse('User list')
Browser access address
http://127.0.0.1:8000/api/v1/users/
Then the background gets the version information
3, url for reverse parsing access
(1)api/urls.py
Add name = 'API user'
# 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
# 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): #Get version print(request.version) #Get object to process version print(request.versioning_scheme) #Get the url accessed by the browser, reverse parsing #Two parameters are required: viewname is the alias in the url, and request=request is the parameter to be passed in the url #(? P < version > [v1| V2] +) / users /. The parameter of version should be passed here, but version is included in the request (you can see in the source code). All you need is request=request url_path = request.versioning_scheme.reverse(viewname='api_user',request=request) print(url_path) # self.dispatch return HttpResponse('User list')
Browser access
http://127.0.0.1:8000/api/v1/users/
Background acquisition
4, Source flow
(1)dispatch
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 #Processing the original request enriches some functions #Request( # request, # parsers=self.get_parsers(), # authenticators=self.get_authenticators(), # negotiator=self.get_content_negotiator(), # parser_context=parser_context # ) #Request (original request,[BasicAuthentications object,]) #Get native request, request #Get the object of the authentication class, request.authenticators #1. Package request request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: #2. Authentication, version Verification 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
(3)initial
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 get version information #Request.versioning scheme get the processing version of your object # No matter which class gets the version, it gets the version by calling the determine? Version function version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted #4. Achieve authentication self.perform_authentication(request) #5. Authority judgment self.check_permissions(request) #6. Control access frequency self.check_throttles(request)
(3)determine_version
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
If there is no versioning u class in the instance's class, it will be found in the configuration file setting by default
5, URLPathVersioning source code
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 )
Can see
(1)url configuration
(2)determine_version
There is an is "allowed" version in it. Click here to see some basic parameters (inherit the BaseVersioning base class)
class BaseVersioning(object): #Default version default_version = api_settings.DEFAULT_VERSION #Allowed versions allowed_versions = api_settings.ALLOWED_VERSIONS #Default parameter (version, for example, you can customize it to 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))