码迷,mamicode.com
首页 > 其他好文 > 详细

Django Rest Framewoek

时间:2019-10-28 12:21:16      阅读:134      评论:0      收藏:0      [点我收藏+]

标签:framework   this   toc   ===   available   view   程序   you   ip地址   

1 什么是RESTful

  • REST 指的是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。满足这些约束条件和原则的应用程序或设计就是 RESTful。

2 RESTful设计

  • API与用户的通信协议,总是使用HTTPS协议。

  • 域名

    • https://api.example.com 子域名的方式,会存在跨域问题

    • https://example.org/api/ 更简单(推荐)

  • 版本

    • URL,如:https://example.org/api/v1/

    • 请求头,跨域时,引发发送多次请求

  • 路径,视网络上任何东西都是资源,均使用名词表示(可复数)

    • https://api.example.com/v1/zoos

    • https://api.example.com/v1/animals

    • https://api.example.com/v1/employees

  • method

    • GET :从服务器取出资源(一项或多项)

    • POST:在服务器新建一个资源

    • PUT:在服务器更新资源(客户端提供改变后的完整资源)

    • PATCH:在服务器更新资源,局部更新,比如:只更新某个字段

    • DELETE:从服务器删除资源

  • 过滤,通过在url上传参的形式传递搜索条件

    • https://api.example.com/v1/zoos?limit=10:指定返回记录的数量

    • https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置

    • https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数

    • https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序

    • https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件

  • 状态码

    200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
    201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
    202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
    204 NO CONTENT - [DELETE]:用户删除数据成功。
    400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
    401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
    403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
    404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
    406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
    410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
    422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
    500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
    ?
    # 状态码可以自己自定义
        - return HttpResponse(json.dumps(ret),status=800)  # 请求成功会返回800
    状态码和code结合的情况。
    更多看这里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
  • 错误处理,状态码是4xx时,应返回错误信息,error当做key。

    {
        error: "Invalid API key"
    }
  • 返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范。

    GET /collection:返回资源对象的列表(数组)
    GET /collection/1/:返回单个资源对象
    POST /collection:返回新生成的资源对象
    PUT /collection/1/:返回完整的资源对象
    PATCH /collection/1/:返回完整的资源对象
    DELETE /collection/1/:返回一个空文档
  • Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。

    {"link": {
      "rel":   "collection https://www.example.com/zoos",
      "href":  "https://api.example.com/zoos",
      "title": "List of zoos",
      "type":  "application/vnd.yourformat+json"
    }}

3 基于Django Rest Framework框架实现

3.1 安装:

pip3 install djangorestframework

3.2 基本流程

  • 先在django的settings.py中注册rest_framework

    INSTALLED_APPS = [
        ...
        rest_framework,
    ]
  • urls.py

    from django.conf.urls import url, include
    from web.views.s1_api import TestView
     
    urlpatterns = [
        url(r^test/, TestView.as_view()),
    ]
  • views.py

    from rest_framework.views import APIView
    from rest_framework.response import Response
     
    class TestView(APIView):
        def dispatch(self, request, *args, **kwargs):
            """
            请求到来之后,都要执行dispatch方法,dispatch方法根据请求方式不同触发 get/post/put等方法
            注意:APIView中的dispatch方法有好多好多的功能
            """
            return super().dispatch(request, *args, **kwargs)
     
        def get(self, request, *args, **kwargs):
            return Response(GET请求,响应内容)
     
        def post(self, request, *args, **kwargs):
            return Response(POST请求,响应内容)
     
        def put(self, request, *args, **kwargs):
            return Response(PUT请求,响应内容)
        def delete(self, request, *args, **kwargs):
            return Response(DELETE请求,响应内容)

3.3 认证

  • 用户url传入内容认证

    技术图片
    # CBV首先要走的dispatch方法,所以重写dispatch中的内容可以让代码执行前自定义执行某些内容。
    ?
    class MyAuthentication(object):
        def authenticate(self,request):
            username = request._request.GET.get("username")
            print(username)
            obj = models.UserInfo.objects.filter(user=username).first()
            if not obj:
                raise exceptions.AuthenticationFailed("用户认证失败")
            return (obj.user,None)
        def authenticate_header(self,val):
            pass
    ?
    class MyIndex(APIView):
        authentication_classes = [MyAuthentication]  # 加了这个就必须先执行定义的类
        def get(self, request, *args, **kwargs):
            return Response({"get":"get请求"})
        def post(self, request, *args, **kwargs):
            return Response({"post":"post请求"})
        def put(self, request, *args, **kwargs):
            return Response({"put":"put请求"})
        def delete(self, request, *args, **kwargs):
            return Response({"delete":"delete请求"})
    View Code
  • 基于token实现的用户认证

    技术图片
    # model
    from django.db import models
    ?
    class User(models.Model):
        username = models.CharField(max_length=12)
        
    class Usertoken(models.Model):
        user = models.OneToOneField(to="User")
        token = models.CharField(max_length=64)
    model
    技术图片
    from rest_framework import exceptions
    from rest_framework.response import Response
    from rest_framework.views import APIView
    from app01 import models
    class MyAuthentication(object):
        def authenticate(self,request):
            token = request._request.GET.get(token)
            token_obj = models.UserToken.objects.filter(token=token).first()
            if obj:
                return (token_obj.user,token_obj)  # 将元组中user对象和token对象封装到request中
            else:
                raise exceptions.AuthenticationFailed("用户认证失败")
        def authenticate_header(self,val):
            pass
    class Order(APIView):
        authentication_classes = [MyAuthentication,]
        def get(self,request,*args,**kwargs):
            # reuqest.user==》token_obj.user
            # request.auth==》token_obj
            return Response({"ret":"请求成功"})
    View Code
  • 认证有三种返回值,一种是返回元组,赋值分别赋值给user和auth封装到request中,一种是返回None,交给下一个认证处理,如果都返回None,那么返回的是一个匿名用户,如果出错则抛出异常。

  • 全局使用,在settings中做如下配置。

    技术图片
    # 在rest_framework的settings.py中,最上面有一段话,例如,项目的“settings.py”文件可能如下所示:
    ?
    REST_FRAMEWORK = {"DEFAULT_AUTHENTICATION_CLASSES":[xxx.xxx.MyAuthentication]}
    # 和默认DEFAULTS是一样的写法,只是在我们自己的配置文件中
    #此模块用于访问REST framework的配置,先检查用户设置,再检查默认设置DEFAULTS
    View Code
  • 匿名用户

    技术图片
    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES":[xxx.xxx],
        "UNAUTHENTICATED_USER":None,  # 匿名或者未登录reuqest.user=None
        "UNAUTHENTICATED_TOKEN":None, # 匿名或者未登录request.auth=None
    }
    View Code
  • 内置认证类

    使用的时候都继承BaseAuthentication

    技术图片
    from rest_framework.authentication import BaseAuthentication
    class MyAuthentication(BaseAuthentication):
        def authenticate(self,request):
            token = request._request.GET.get(token)
            obj = models.UserToken.objects.filter(token=token).first()
            if obj:
                return (obj.user,obj)  # 将元组中的数据封装到request中
            else:
                raise exceptions.AuthenticationFailed("用户认证失败")
        # 直接继承这个类不需要写authenticate_header方法了
    View Code

    BasicAuthentication浏览器对用户名密码加密,放在请求头中发过去的

  • 源码解析

    技术图片
    1.django中的类先执行dispatch方法,Request()对象中封装了原始的self._request=request和authenticators(是一个列表,里面是一个个对象)。authenticators实际上是authentication_classes一个全局配置,使用列表推导式得到的。所以我们要将我们的认证类注册到authentication_classes中,写在我们需要认证的类中。这就是局部认证。全局认证需要我们在配置中按照源码中的配置进行相应的修改,写在我们的配置中。
    2.执行initial方法,执行perform_authentication就是用户认证
    3.执行request中的user属性
    4.执行self._authenticate()方法,循环authenticators,执行authenticate方法,这个方法是必须要写的,也就是我们写认证类时候必须要写的方法,还有一个方法是authenticate_header。需要返回一个元组,将返回值赋值给user和auth
    5._not_authenticated方法就是匿名用户
    6.实际上我们重写的就是BaseAuthentication类中的方法,所以我们要继承这个类。
    7.而其他继承BaseAuthentication这个类的子类,就是内置认证类。
    View Code

3.4权限

  • 权限基本实现

    技术图片
    # model
    from django.db import models
    ?
    class User(models.Model):
        usertype_choice = (
            (1,"普通用户"),
            (2,"vip用户"),
            (3,"svip用户"),
        )
        usertype = models.IntegerField(choices=usertype_choice)
        user = models.CharField(max_length=12)
    model
    技术图片
    # 局部添加权限
    ?
    from rest_framework.permissions import BasePermission
    ?
    class Mypermission(BasePermission):
        message = "必须是超级VIP才能访问"  # 这句话就是抛出异常的信息。
        def has_permission(self, request, view):
            if request.user.usertype != 3:  # 不是svip的就没有权限
                return False # 没权限
            return True # 有权限
    ?
    class Order(APIView):
        permission_classes = [Mypermission,]
        def get(self,request,*args,**kwargs):
            return Response({"ret":"请求成功"})
    View Code
  • 全局添加权限

    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES":[xxx.xxx.MyAuthentication], # 认证
        "DEFAULT_AUTHENTICATION_CLASSES":[xxx.xxx.Mypermission],  # 权限
    }

3.5 访问频率的限制(节流)

  • 匿名用户,我们自己实现的

    技术图片
    # 通过ip限制
    from rest_framework.throttling import BaseThrottle
    ?
    VISIT_RECORD = {}  # 最终放在缓存中
    LIMIT_TIME = 10
    FREQUENCY = 3
    ?
    class VisitThrottle(BaseThrottle):
        """LIMIT_TIME时间内只能访问FREQUENCY次"""
        def __init__(self):
            self.history = None
    ?
        def allow_request(self, request, view):
            # 获取访问用户的ip地址
            remote_addr = request.META.get(REMOTE_ADDR)
            print(remote_addr)
            ctime = time.time()
            if remote_addr not in VISIT_RECORD:
                VISIT_RECORD[remote_addr] = [ctime, ]
                return True
            history = VISIT_RECORD.get(remote_addr)
            self.history = history
            while history and ctime - history[-1] > LIMIT_TIME:
                history.pop()
            if len(history) < FREQUENCY:
                history.insert(0, ctime)
                return True
        # 再等多久之后就可以访问了,每次刷新都变化,动态的
        def wait(self):
            ctime = time.time()
            # 当当前时间和最早一次访问的时间差等于LIMIT_TIME时间的时候,就可以再次访问
            return LIMIT_TIME-(ctime-self.history[-1])  
        
        class Index(APIView):
            # 局部限制
            throttle_classes = [VisitThrottle,]
    ?
            def get(self, request, *args, **kwargs):
                print(request.user, request.auth, "******")
                return Response(GET请求,响应内容)
    View Code
  • 全局配置

    技术图片
    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES":[xxx.xxx.MyAuthentication], # 认证
        "DEFAULT_AUTHENTICATION_CLASSES":[xxx.xxx.Mypermission],  # 权限
        "DEFAULT_THROTTLE_CLASSES":[xxx.xxx.VisitThrottle], # 限流
    }
    View Code
  • 内置访问频率限制

    SimpleRateThrottle

    技术图片
    # 源码
    class SimpleRateThrottle(BaseThrottle):
        """
        A simple cache implementation, that only requires `.get_cache_key()`
        to be overridden.
    ?
        The rate (requests / seconds) is set by a `rate` attribute on the View
        class.  The attribute is a string of the form ‘number_of_requests/period‘.
    ?
        Period should be one of: (‘s‘, ‘sec‘, ‘m‘, ‘min‘, ‘h‘, ‘hour‘, ‘d‘, ‘day‘)
    ?
        Previous request information used for throttling is stored in the cache.
        """
        cache = default_cache
        timer = time.time
        cache_format = throttle_%(scope)s_%(ident)s
        scope = None
        # 4.这个眼熟啊,就是我们rest_frame的配置项,我们就需要在我们的配置文件中添加DEFAULT_THROTTLE_RATES
        THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES
    ?
        def __init__(self):
            # 1.找rate,没有执行get_rate()
            if not getattr(self, rate, None):
                # 6.self.rate = "3/m"
                self.rate = self.get_rate()
                # 7.将"3/m"给到parse_rate方法
                # 12.从第11步拿到元组(3,60),分别赋值self.num_requests=3,self.duration=60
            self.num_requests, self.duration = self.parse_rate(self.rate)
    ?
        def get_cache_key(self, request, view):
            """
            Should return a unique cache-key which can be used for throttling.
            Must be overridden.
    ?
            May return `None` if the request should not be throttled.
            """
            raise NotImplementedError(.get_cache_key() must be overridden)
    ?
        def get_rate(self):
            """
            Determine the string representation of the allowed request rate.
            """
            # 2.又找scope,但是scope是None,会抛出异常,说你必须有一个rate或者scope,所以我们用这个类的时候必须给scope赋值
            if not getattr(self, scope, None):
                msg = ("You must set either `.scope` or `.rate` for ‘%s‘ throttle" %
                       self.__class__.__name__)
                raise ImproperlyConfigured(msg)
    ?
            try:
                # 3.走到这以后让我们从THROTTLE_RATES取scope,THROTTLE_RATES是我们的类变量
                # 5.从我们设置的配置中获取到scope的值,返回给rate
                return self.THROTTLE_RATES[self.scope]
            except KeyError:
                msg = "No default throttle rate set for ‘%s‘ scope" % self.scope
                raise ImproperlyConfigured(msg)
    ?
        def parse_rate(self, rate):
            """
            Given the request rate string, return a two tuple of:
            <allowed number of requests>, <period of time in seconds>
            """
            if rate is None:
                return (None, None)
            # 8.rate = ‘3/m’,不为空进行字符串切割num = "3",period = "m"
            num, period = rate.split(/)
            # 9.num_requests = 3
            num_requests = int(num)
            # 10.从字典中取出period[0] = "m",即duration = 60
            duration = {s: 1, m: 60, h: 3600, d: 86400}[period[0]]
            # 11.将元组(3,60)返回给第7步
            return (num_requests, duration)
        # 13.通过dispatch方法,可以看出必须要有allow_request方法,执行allow_request方法
        def allow_request(self, request, view):
            """
            Implement the check to see if the request should be throttled.
    ?
            On success calls `throttle_success`.
            On failure calls `throttle_failure`.
            """
            if self.rate is None:
                return True
            # 14.执行get_cache_key方法,即该方法必须写,
            self.key = self.get_cache_key(request, view)
            if self.key is None:
                return True
            # 15.从django内置缓存中获取到key对应的值(我们将ip或者userid作为key,时间记录列表作为值,那么我们在写get_cache_key方法时就要返回一个ip或者userid,即父类的get_ident方法,就是返回的ip),就是访问时间的记录。
            self.history = self.cache.get(self.key, [])
            self.now = self.timer()
    ?
            # Drop any requests from the history which have now passed the
            # throttle duration
            # 16.当历史值存在,有访问记录,当第一次访问的时间小于等于当前时间-步骤11的60,证明不是duration时间内的记录,是很久之前的,就将他从列表中删除
            while self.history and self.history[-1] <= self.now - self.duration:
                self.history.pop()
            # 17.历史记录的长度大于等于我们设定的次数,执行throttle_failure方法,返回Flase
            if len(self.history) >= self.num_requests:
                return self.throttle_failure()
            # 18.长度比我们设定的次数小则将当前时间插入到列表第一个位置,并返回True
            return self.throttle_success()
    ?
        def throttle_success(self):
            """
            Inserts the current request‘s timestamp along with the key
            into the cache.
            """
            self.history.insert(0, self.now)
            self.cache.set(self.key, self.history, self.duration)
            return True
    ?
        def throttle_failure(self):
            """
            Called when a request to the API has failed due to throttling.
            """
            return False
    ?
        def wait(self):
            """
            Returns the recommended next request time in seconds.
            """
            if self.history:
                remaining_duration = self.duration - (self.now - self.history[-1])
            else:
                remaining_duration = self.duration
    ?
            available_requests = self.num_requests - len(self.history) + 1
            if available_requests <= 0:
                return None
    ?
            return remaining_duration / float(available_requests)
    View Code

    匿名用户内置限流器

    技术图片
    # 内置限流器我们只需要实现scope这个类变量并重写get_cache_key方法返回self.get_ident(request),然后在配置中进行下面的配置即可。这是匿名用户的内置限流器
    class VisitThrottle(SimpleRateThrottle):
        scope = "love"  # 这个名字可以自定义
        def get_cache_key(self, request, view):
            return self.get_ident(request)  
        
    class Index(APIView):
        throttle_classes = [VisitThrottle,]
    ?
        def get(self, request, *args, **kwargs):
            print(request.user, request.auth, "******")
            return Response(GET请求,响应内容)
    View Code

    配置文件

    技术图片
    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES":[xxx.xxx.MyAuthentication], # 认证
        "DEFAULT_AUTHENTICATION_CLASSES":[xxx.xxx.Mypermission],  # 权限
        "DEFAULT_THROTTLE_RATES":{"love":"3/m"}, # 匿名用户限流的
    }
    View Code

    登录用户内置限流器

    技术图片
    # 登录的内置限流器,要继承UserRateThrottle,这个类继承的SimpleRateThrottle
    class UserThrottle(UserRateThrottle):
        scope = "user"  # 使用用户名,或者用户id
        def get_cache_key(self, request, view):
            # 我们在认证的时候知道request中封装了user和auth,request.user就是user对象
            return request.user.username  # 当前登录用户的用户名
        
    class Index(APIView):
        throttle_classes = [UserThrottle,]
    ?
        def get(self, request, *args, **kwargs):
            print(request.user, request.auth, "******")
            return Response(GET请求,响应内容)
    View Code

    配置文件

    技术图片
    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES":[xxx.xxx.MyAuthentication], # 认证
        "DEFAULT_AUTHENTICATION_CLASSES":[xxx.xxx.Mypermission],  # 权限
        "DEFAULT_THROTTLE_RATES":{"love":"3/m"}, # 匿名用户限流的,一分钟访问三次
        "DEFAULT_THROTTLE_RATES":{"user":"10/m"}, # 登录用户限流,一分钟访问10次
    }
    View Code

    既有匿名用户也有登录用的情况

    技术图片
    # 登录的内置限流器,要继承UserRateThrottle,这个类继承的SimpleRateThrottle
    class VisitThrottle(SimpleRateThrottle):
        scope = "love"  # 这个名字可以自定义
        def get_cache_key(self, request, view):
            return self.get_ident(request)  
    ?
    class UserThrottle(UserRateThrottle):
        scope = "user"  
        def get_cache_key(self, request, view):
            # 我们在认证的时候知道request中封装了user和auth,request.user就是user对象
            return request.user.username  # 当前登录用户的用户名
        
    class Index(APIView):
        authentication_classes = [MyAuthentication, ]
        throttle_classes = [VisitThrottle,]
    ?
        def get(self, request, *args, **kwargs):
            print(request.user, request.auth, "******")
            return Response(GET请求,响应内容)
    View Code
    技术图片
    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES":[xxx.xxx.MyAuthentication], # 认证
        "DEFAULT_AUTHENTICATION_CLASSES":[xxx.xxx.Mypermission],  # 权限
        "DEFAULT_THROTTLE_CLASSES":[xxx.xxx.UserThrottle],  # 登录用户全局设置限流
        # 登录用户可以访问所有,对全局进行限制,匿名用户只能访问部分功能,只对部分功能做匿名用户限流即可,所有匿名用户使用局部方法,如果是匿名用户会优先选择局部的方法进行限流。
        "DEFAULT_THROTTLE_RATES":{"love":"3/m"}, # 匿名用户限流的,一分钟访问三次
        "DEFAULT_THROTTLE_RATES":{"user":"10/m"}, # 登录用户限流,一分钟访问10次
    }
    settings.py

3.6 drf中的request

  • 通过我们前几个组件可以看出drf中的request对象并不是我们django中以前使用的request,drf中的request对象封装了我们以前用的request。封装到了_request中,drf通过request._request才是我们以前用的request,所以drf中很多方法属性都要通过request._request获取,但是其实不需要,我们依然可以直接使用request,我们看一下drf源码

    技术图片
    from rest_framework.request import Request  # 从Request中找到下面方法
    # drf中的__getattr__方法
    def __getattr__(self, attr):
        """
        If an attribute does not exist on this instance, then we also attempt
        to proxy it to the underlying HttpRequest object.
        """
        try:
            # 如果执行我们drf中request没有的方法或属性,就会执行__getattr__方法,然后去request._request中去找对应的方法,即我们原始的request对象中去找
            return getattr(self._request, attr)  
        except AttributeError:
            return self.__getattribute__(attr)
    View Code
  • 当然drf中也封装了很多常用的方法和属性,示例

    技术图片
    from rest_framework.request import Request  # 从Request中找到下面属性
    ?
    @property
    def query_params(self):
        """
        More semantically correct name for request.GET.
        """
        return self._request.GET
    通过request.query_params ===> request._request.GET
    request.query_params.get("xxx") # 取url上的参数
    View Code

3.7 版本组件

  • 版本通过url上传参?version=v1

    自定义版本获取

    技术图片
    # ?version=v1
    class MyVersion(object):
        def determine_version(self, request, *args, **kwargs):
            request.query_params.get("version")
            return version
    class User(APIView):
        # 局部视图使用
        versioning_class = MyVersion
        def get(self, request, *args, **kwargs):
            print(request.version, "******")
            return Response(GET请求,响应内容)
    View Code

    内置类版本获取

    技术图片
    from rest_framework.versioning import QueryParameterVersioning
    ?
    class User(APIView):
        # 局部视图使用
        versioning_class = QueryParameterVersioning
        def get(self, request, *args, **kwargs):
            print(request.version, "******")
            return Response(GET请求,响应内容)
        
    # 配置文件中
    REST_FRAMEWORK = {
        "DEFAULT_VERSION":"v1",# 默认版本
        "ALLOWED_VERSIONS":[v1,v2], # 允许的版本
        "VERSION_PARAM":"version",# 表示的参数的键是什么,比如?version=v1,设置的就是version
    }
    局部示例
    技术图片
    # 全局使用
    ?
    class User(APIView):
        def get(self, request, *args, **kwargs):
            print(request.version, "******")
            return Response(GET请求,响应内容)
        
    # 配置文件中
    REST_FRAMEWORK = {
        "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.QueryParameterVersioning",
        "DEFAULT_VERSION":"v1",# 默认版本
        "ALLOWED_VERSIONS":[v1,v2], # 允许的版本
        "VERSION_PARAM":"version",# 表示的参数的键是什么,比如?version=v1,设置的就是version
    }
    全局配置应用
  • 版本通过url上路径传参 /v1/user/,推荐这种

    内置版本类

    技术图片
    # urls.py要进行修改
    # url上要对应改变
    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    ?
    urlpatterns = [
        path(admin/, admin.site.urls),
        url(r^(?P<version>[v1|v2]+)/user/$, views.User.as_view(), name=users-list),
    ]
    ?
    # 视图函数中.py
    from rest_framework.versioning import URLPathVersioning
    class User(APIView):
        # 局部视图使用
        versioning_class = URLPathVersioning
        def get(self, request, *args, **kwargs):
            print(request.version, "******")
            return Response({"ret":GET请求,响应内容})
        
    # settings.py
    REST_FRAMEWORK = {
        "DEFAULT_VERSION":"v1",# 默认版本
        "ALLOWED_VERSIONS":[v1,v2], # 允许的版本
        "VERSION_PARAM":"version",# 表示的参数的键是什么,比如?version=v1,设置的就是version
    }
    局部示例
    技术图片
    # 全局配置
    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    ?
    urlpatterns = [
        path(admin/, admin.site.urls),
        url(r^(?P<version>[v1|v2]+)/user/$, views.User.as_view(), name=users-list),
    ]
    ?
    # 视图函数中.py
    class User(APIView):
        def get(self, request, *args, **kwargs):
            print(request.version, "******")
            return Response({"ret":GET请求,响应内容})
        
    # settings.py
    REST_FRAMEWORK = {
        "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning"
        "DEFAULT_VERSION":"v1",# 默认版本
        "ALLOWED_VERSIONS":[v1,v2], # 允许的版本
        "VERSION_PARAM":"version",# 表示的参数的键是什么,比如?version=v1,设置的就是version
    }
    全局配置应用
  • 源码流程

    技术图片
    1.先执行dispatch方法,执行其中的initial方法
    2.initial方法中部分源码
        # Determine the API version, if versioning is in use.
        # 获取到版本,和处理版本的对象
        version, scheme = self.determine_version(request, *args, **kwargs)
        # 并将版本号和对象赋值到request,从request中就可以拿到version和处理版本的对象scheme
        request.version, request.versioning_scheme = version, scheme
        
    3.determine_version方法就是去实例化我们处理版本的类,并但会版本和处理版本的对象。
    4.现在drf执行流程,先进行版本处理--》认证--》权限--》限流
    # 在URLPathVersioning或者QueryParameterVersioning中有一个reserve方法,用于反向解析找到url的
    # urls.py
    urlpatterns = [
        path(admin/, admin.site.urls),
        url(r^(?P<version>[v1|v2]+)/user/$, views.User.as_view(), name=users-list),
    ]
    View Code
    # 视图函数中.py
    class User(APIView):
        def get(self, request, *args, **kwargs):
            print(request.version, "******")
            # request.versioning_scheme就是我们处理按本的对象,按照我们自己写的反向解析,传参要将url上正则的分组命名的关键字参数传入,但是使用drf的reserve不需要再传了,我们只需要传个request即可,request里面有个version,自己会加上。没有指定版本会生成当前url的版本
            u1 = request.versioning_scheme.reserve(viewname="users-list",request=request)
            print(u1)
            
            # django的reserve
            from django.urls import reverse
            reserve(viewname="users-list",kwargs={"version":"v1"})
            return Response({"ret":GET请求,响应内容}) 

    内置的版本处理有哪些方式?

    技术图片
    QueryParameterVersioning基于url参数做版本处理 如:GET /something/?version=0.1 HTTP/1.1
    HostNameVersioning基于域名做的版本处理,如:v1.example.com
    NamespaceVersioning基于命名空间做的版本处理,如:
        urlpatterns = [
            url(r^v1/, include(users.urls, namespace=v1)),
            url(r^v2/, include(users.urls, namespace=v2))
        ]
    URLPathVersioning基于url路径做的版本处理,如:/v1/user/ ,推荐使用
    AcceptHeaderVersioning基于请求头处理的,如:Accept: application/json; version=1.0
    View Code
  • 总结

    技术图片
    1.进行配置
    REST_FRAMEWORK = {
        "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning"
        "DEFAULT_VERSION":"v1",# 默认版本
        "ALLOWED_VERSIONS":[v1,v2], # 允许的版本
        "VERSION_PARAM":"version",# 表示的参数的键是什么,比如?version=v1,设置的就是version
    }
    2.urls.py,如果有路由分发再使用路由分发
    urlpatterns = [
        path(admin/, admin.site.urls),
        url(r^(?P<version>[v1|v2]+)/user/$, views.User.as_view(), name=users-list),
    ]
    3.视图中使用
    class User(APIView):
        def get(self, request, *args, **kwargs):
            # 1.获取版本
            print(request.version, "******")
            # 2.获取版本处理的对象
            print(request.versioning_scheme)
            # 3.通过drf中的reserve使用版本处理对象进行反向解析
            u1 = request.versioning_scheme.reserve(viewname="users-list",request=request)
            print(u1)
            
            # 4.使用django提供的reserve
            from django.urls import reverse
            reserve(viewname="users-list",kwargs={"version":"v1"})
            return Response({"ret":GET请求,响应内容})
    View Code

     

未完待续...

Django Rest Framewoek

标签:framework   this   toc   ===   available   view   程序   you   ip地址   

原文地址:https://www.cnblogs.com/liuweida/p/11749572.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!