current position:Home>Django API Version (II)

Django API Version (II)

2022-01-31 11:43:19 Waiting for

This is my participation 11 The fourth of the yuegengwen challenge 10 God , Check out the activity details :2021 One last more challenge

One 、 Source analysis

It's the same as the certification process , Please come in , Go the same way APIview Of dispatch Methods , Please read the notes section :

1.APIView Class dispatch Source code :

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
        # Original request Carry on the processing , Enriched 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._request# Get the object of the authentication class ,request.authticators#1. encapsulation request
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?try:
            self.initial(request, *args, **kwargs)

            # Get the appropriate handler methodif request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                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

 Copy code 

2. Then perform self.inital Method :

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.#### version control 
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        # Ensure that the incoming request is permitted#2. Achieve Authentication  self.perform_authentication(request)
        #3. Authority judgment  self.check_permissions(request)
        #4. Frequency limit 
 Copy code 

3. You can see that version control is before authentication , First, execute version, scheme = self.determine_version(request, *args, **kwargs), Here are self.determine_version Source code :

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:  # First judge whether the version class exists (self.versioning_class  Whether it exists ), There is no return tuple,(none,none)
            return (None, None)            
        scheme = self.versioning_class()   # There is a return version class object 
        return (scheme.determine_version(request, *args, **kwargs), scheme) # Version class exists , Finally, return the version of the class object determine_version Method result ( That is, the returned version number ), And class objects ,
                                                                              This is the method that each version class must have , Used to get the version .
 Copy code 

4. Undertaking  self.determine_version After the method is executed , Then perform request.version, request.versioning_scheme = version, scheme, That goes without saying , Nothing more than assigning the version number to request.version attribute , The version class object is assigned to request.versioning_scheme, That's why we can pass request.version Reason for obtaining version number .

5. The same as the authentication source code ,self.determine_version Method self.versioning_class(), There are also configurations in the global

class APIView(View):

    # The following policies may be set at either globally, or per-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  # Version processing class configuration 
 Copy code 

6. Based on the above source code analysis, after , Now let's analyze , The two versioning classes used in our example , Please refer to the notes for specific analysis :


class QueryParameterVersioning(BaseVersioning):
    """ GET /something/?version=0.1 HTTP/1.1 Host: Accept: application/json """
    invalid_version_message = _('Invalid version in query parameter.')  ##  When When the allowed version is configured , Error message returned by mismatched version , You can define yourself 

    def determine_version(self, request, *args, **kwargs):           ##  Get version method 
        version = request.query_params.get(self.version_param, self.default_version) #  adopt request.query_paras Method to get ( The essence request.MATE.get),
                                                                                       default_version The default is version, Is in settings Configured in 
        if not self.is_allowed_version(version):     # The disallowed version throws an exception 
            raise exceptions.NotFound(self.invalid_version_message)
        return version  # If there is no exception, the version number is returned 

    def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):  #url  Anti parsing , This method can be used to generate the requested url, There will be examples later 
        url = super(QueryParameterVersioning, self).reverse(
            viewname, args, kwargs, request, format, **extra
        if request.version is not None:
            return replace_query_param(url, self.version_param, request.version)   
        return url

 Copy 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: Accept: application/json """
    invalid_version_message = _('Invalid version in URL path.')  #  Disallowed version information , Customizable 

    def determine_version(self, request, *args, **kwargs):    ##  The same implementation determine_version Method to get the version 
        version = kwargs.get(self.version_param, self.default_version) #  Because the delivered version is in url In the regular of , So from kwargs In order to get ,self.version_param The default is version
        if not self.is_allowed_version(version):
            raise exceptions.NotFound(self.invalid_version_message)     #  Didn't get , Throw an exception 
        return version                                                  #  Normal acquisition , Return version number 

    def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra): # url Anti parsing , There will be examples later 
        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

 Copy code 

This version class inherits BaseVersioning:

class BaseVersioning(object):
    default_version = api_settings.DEFAULT_VERSION            # Silent person version configuration 
    allowed_versions = api_settings.ALLOWED_VERSIONS      # Allow version configuration 
    version_param = api_settings.VERSION_PARAM                # edition key To configure 

    def determine_version(self, request, *args, **kwargs):
        msg = '{cls}.determine_version() must be implemented.'raise NotImplementedError(msg.format(

    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))

 Copy code 

Two 、 Use version inversion to generate URL

With URLPathVersioning For example , Its essence is also used django Of url Reverse parsing method , There is no need to explain the implementation process here , If you are interested, you can see the source code yourself .

1. To configure url, by view Take the alias

urlpatterns = [

    url(r'^api/v1/auth', views.AuthView.as_view()),
    url(r'^api/v1/order', views.OrderView.as_view()),
    url(r'^api/(?P<version>[v1|v2]+)/user', views.UserView.as_view(),name="user_view"),
 Copy code 

2. utilize reverse Method reversely generates the requested url,UserView View .

class UserView(APIView):
    ''' View user information '''from rest_framework.versioning import URLPathVersioning

    versioning_class =URLPathVersioning
    def get(self,request,*args,**kwargs):

        url = request.versioning_scheme.reverse(viewname='user_view', request=request)
        #versioning_scheme It has been analyzed in the source code , Is the object instantiated by the version class print(url)
        return JsonResponse(res,safe=True)

 Copy code 

Use postman Send the request : The results are as follows :

3、 ... and  、 summary

For version control , In fact, there is no need to define or write version processing classes by yourself , Global configuration is recommended , as well as URLPathVersioning class .

Specific configuration :

#  Global configuration 
     "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",  # Path to class "DEFAULT_VERSION":'v1', # Default version "ALLOWED_VERSIONS":['v1','v2'], # Allowed versions  # "VERSION_PARAM":'version' # Use QueryParameterVersioning When to configure ,get Parameters passed during the request key }

# A single view 
versioning_class =URLPathVersioning
 Copy code 

copyright notice
author[Waiting for],Please bring the original link to reprint, thank you.

Random recommended