zoukankan      html  css  js  c++  java
  • Rest Framework

    一、RESTful 规范

    •  什么是RESTful规范:

      •  REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
      •  REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态
      •  REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
      •  所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性
      •  对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)
    • RESTful API 设计:

      •    API与用户的通信协议,总是使用HTTPs协议
      •   域名
        • https://api.example.com                         尽量将API部署在专用域名(会存在跨域问题)
        • https://example.org/api/                        API很简单
      •   版本 
        • URL,如:https://api.example.com/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 - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
    
          更多看这里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
          '''
      •   错误处理,应返回错误信息,error当做key。
      •   返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范。
        • GET /collection:返回资源对象的列表(数组)
        • GET /collection/resource:返回单个资源对象
        • POST /collection:返回新生成的资源对象
        • PUT /collection/resource:返回完整的资源对象
        • PATCH /collection/resource:返回完整的资源对象
        • DELETE /collection/resource:返回一个空文档
      •   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"
          }}
    • 基于Django实现

        
    1 urlpatterns = [
    2     url(r'^users/$', views.Users.as_view()),
    3     url(r'^users2/$', views.user2),
    4 
    5 ]
    路由系统配置
        
     1 import json
     2 
     3 def  user2(request):
     4     if request.method=='GET':
     5         dic = {'status':200,'name': 'lqz2', 'age': 18}
     6         return HttpResponse(json.dumps(dic))
     7     elif request.method=='POST':
     8         dic = {'status': 200, 'msg': '修改成功'}
     9         return JsonResponse(dic)
    10 
    11 class Users(View):
    12     def get(self, request):
    13         dic = {'status':200,'name': 'lqz', 'age': 18}
    14         return HttpResponse(json.dumps(dic))
    15 
    16     def post(self, request):
    17         dic = {'status': 200, 'msg': '修改成功'}
    18         return JsonResponse(dic)
    views.py

    二、APIView 组件

    •  安装djangorestframework

      • 方式一:pip3 install djangorestframework
      • 方式二:pycharm图形化界面安装
      • 方式三:pycharm命令行下安装(装在当前工程所用的解释器下)
    • djangorestframework的APIView分析

     1 @classmethod
     2     def as_view(cls, **initkwargs):
     3         """
     4         Store the original class on the view function.
     5 
     6         This allows us to discover information about the view when we do URL
     7         reverse lookups.  Used for breadcrumb generation.
     8         """
     9         if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
    10             def force_evaluation():
    11                 raise RuntimeError(
    12                     'Do not evaluate the `.queryset` attribute directly, '
    13                     'as the result will be cached and reused between requests. '
    14                     'Use `.all()` or call `.get_queryset()` instead.'
    15                 )
    16             cls.queryset._fetch_all = force_evaluation
    17 
    18         view = super(APIView, cls).as_view(**initkwargs)
    19         view.cls = cls
    20         view.initkwargs = initkwargs
    21 
    22         # Note: session based authentication is explicitly CSRF validated,
    23         # all other authentication is CSRF exempt.
    24         return csrf_exempt(view)
    as_view方法
     1 def dispatch(self, request, *args, **kwargs):
     2         """
     3         `.dispatch()` is pretty much the same as Django's regular dispatch,
     4         but with extra hooks for startup, finalize, and exception handling.
     5         """
     6         self.args = args
     7         self.kwargs = kwargs
     8         request = self.initialize_request(request, *args, **kwargs)
     9         self.request = request
    10         self.headers = self.default_response_headers  # deprecate?
    11 
    12         try:
    13             self.initial(request, *args, **kwargs)
    14 
    15             # Get the appropriate handler method
    16             if request.method.lower() in self.http_method_names:
    17                 handler = getattr(self, request.method.lower(),
    18                                   self.http_method_not_allowed)
    19             else:
    20                 handler = self.http_method_not_allowed
    21 
    22             response = handler(request, *args, **kwargs)
    23 
    24         except Exception as exc:
    25             response = self.handle_exception(exc)
    26 
    27         self.response = self.finalize_response(request, response, *args, **kwargs)
    28         return self.response
    dispatch
    def initialize_request(self, request, *args, **kwargs):
            """
            Returns the initial request object.
            """
            parser_context = self.get_parser_context(request)
    
            return Request(
                request,
                parsers=self.get_parsers(),
                authenticators=self.get_authenticators(),
                negotiator=self.get_content_negotiator(),
                parser_context=parser_context
            )
    initialize_request
     1 def initial(self, request, *args, **kwargs):
     2         """
     3         Runs anything that needs to occur prior to calling the method handler.
     4         """
     5         self.format_kwarg = self.get_format_suffix(**kwargs)
     6 
     7         # Perform content negotiation and store the accepted info on the request
     8         neg = self.perform_content_negotiation(request)
     9         request.accepted_renderer, request.accepted_media_type = neg
    10 
    11         # Determine the API version, if versioning is in use.
    12         version, scheme = self.determine_version(request, *args, **kwargs)
    13         request.version, request.versioning_scheme = version, scheme
    14 
    15         # Ensure that the incoming request is permitted
    16         self.perform_authentication(request)
    17         self.check_permissions(request)
    18         self.check_throttles(request)
    initial方法(内部调用认证,权限和频率)

    三、序列化组件

    • rest-framework序列化之Serializer

        
     1 from django.db import models
     2 
     3 # Create your models here.
     4 
     5 
     6 class Book(models.Model):
     7     title=models.CharField(max_length=32)
     8     price=models.IntegerField()
     9     pub_date=models.DateField()
    10     publish=models.ForeignKey("Publish")
    11     authors=models.ManyToManyField("Author")
    12     def __str__(self):
    13         return self.title
    14 
    15 class Publish(models.Model):
    16     name=models.CharField(max_length=32)
    17     email=models.EmailField()
    18     def __str__(self):
    19         return self.name
    20 
    21 class Author(models.Model):
    22     name=models.CharField(max_length=32)
    23     age=models.IntegerField()
    24     def __str__(self):
    25         return self.name
    models.py
        
     1 from rest_framework.views import APIView
     2 from rest_framework.response import Response
     3 from .models import *
     4 from django.shortcuts import HttpResponse
     5 from django.core import serializers
     6 
     7 
     8 from rest_framework import serializers
     9 
    10 class BookSerializers(serializers.Serializer):
    11     title=serializers.CharField(max_length=32)
    12     price=serializers.IntegerField()
    13     pub_date=serializers.DateField()
    14     publish=serializers.CharField(source="publish.name")
    15     #authors=serializers.CharField(source="authors.all")
    16     authors=serializers.SerializerMethodField()
    17     def get_authors(self,obj):
    18         temp=[]
    19         for author in obj.authors.all():
    20             temp.append(author.name)
    21         return temp
    22   #此处可以继续用author的Serializers,
    23   # def get_authors(self,obj):
    24     #     ret=obj.authors.all()
    25     #     ss=AuthorSerializer(ret,many=True)
    26     #     return ss.data
    27 
    28 class BookViewSet(APIView):
    29 
    30     def get(self,request,*args,**kwargs):
    31         book_list=Book.objects.all()
    32         # 序列化方式1:
    33         # from django.forms.models import model_to_dict
    34         # import json
    35         # data=[]
    36         # for obj in book_list:
    37         #     data.append(model_to_dict(obj))
    38         # print(data)
    39         # return HttpResponse("ok")
    40 
    41         # 序列化方式2:
    42         # data=serializers.serialize("json",book_list)
    43         # return HttpResponse(data)
    44 
    45         # 序列化方式3:
    46         bs=BookSerializers(book_list,many=True)     #many=True代表有多条数据,如果只有一条数据,many=False
    47         return Response(bs.data)
    48      # 序列化方式4: 
    49       # ret=models.Book.objects.all().values('nid','title')
    50      # dd=list(ret)
    51         # return HttpResponse(json.dumps(dd))
    views.py

        注意:

          source 如果是字段,会显示字段,如果是方法,会执行方法,不用加括号(authors=serializers.CharField(source='authors.all'))如在模型中定义一个方法,直接可以在在source指定执行

            

          
    class UserInfo(models.Model):
        user_type_choices = (
            (1,'普通用户'),
            (2,'VIP'),
            (3,'SVIP'),
        )
        user_type = models.IntegerField(choices=user_type_choices)
    
        username = models.CharField(max_length=32,unique=True)
        password = models.CharField(max_length=64)
    
    
    #视图
    ret=models.UserInfo.objects.filter(pk=1).first()
    aa=ret.get_user_type_display()
    
    #serializer
    xx=serializers.CharField(source='get_user_type_display')
    View Code
    • rest-framework序列化之ModelSerializer

     1 class BookSerializers(serializers.ModelSerializer):
     2     class Meta:
     3         model = models.Book
     4         # fields = "__all__"
     5         fields=['nid','title','authors','publish']
     6         # exclude=('nid',)   #不能跟fields同时用
     7         # depth = 1    #深度控制,写 几 往里拿几层,层数越多,响应越慢,官方建议0--10之间,个人建议最多3层
     8     publish=serializers.SerializerMethodField()
     9     def get_publish(self,obj):
    10         return obj.publish.name
    11     authors=serializers.SerializerMethodField()
    12     def get_authors(self,obj):
    13         ret=obj.authors.all()
    14         ss=AuthorSerializer(ret,many=True)
    15         return ss.data
    • 生成hypermedialink(极少数)

     1 class BookSerializers(serializers.ModelSerializer):
     2     class Meta:
     3         model = models.Book
     4         fields = "__all__"
     5     # 生成连接,直接查看出版社详情
     6     publish = serializers.HyperlinkedIdentityField(view_name='ttt', lookup_field='publish_id', lookup_url_kwarg='pkk')
     7     authors=serializers.SerializerMethodField()
     8     def get_authors(self,obj):
     9         ret=obj.authors.all()
    10         ss=AuthorSerializer(ret,many=True)
    11         return ss.data
    12 #--------------
    13 
    14 res=BookSerializers(ret,many=True,context={'request': request})
    15 
    16 #--------------
    17 
    18 class Publish(APIView):
    19     def get(self,request,pkk):
    20         print(pkk)
    21         return HttpResponse('ok')
    22 #----路由---
    23 url(r'^publish/(?P<pkk>d+)$', views.Publish.as_view(),name='ttt'),
    • 序列化组件之请求数据校验和保存功能

    class BookSerializers(serializers.ModelSerializer):
        class Meta:
            model=Book
            fields="__all__"
    
    #————————
    class BookView(APIView):
    
        def post(self, request):
    
            # 添加一条数据
            print(request.data)
    
            bs=BookSerializers(data=request.data)
            if bs.is_valid():
                bs.save()  # 生成记录
                return Response(bs.data)
            else:
    
                return Response(bs.errors)
    class BookSerializer1(serializers.Serializer):
        title=serializers.CharField(error_messages={'required': '标题不能为空'})
    
    #这种方式要保存,必须重写create方法

       通过源码查看留的校验字段的钩子函数:

     1 #is_valid---->self.run_validation-(执行Serializer的run_validation)-->self.to_internal_value(data)---(执行Serializer的run_validation:485行)
     2 def validate_title(self, value):
     3         from rest_framework import exceptions
     4         raise exceptions.ValidationError('看你不顺眼')
     5         return value
     6 
     7 #全局
     8 def validate(self, attrs):
     9     from rest_framework import exceptions
    10     if attrs.get('title')== attrs.get('title2'):
    11         return attrs
    12     else:
    13         raise exceptions.ValidationError('不想等啊')
    • 序列化组件源码分析

    1 '''
    2 序列化组件,先调用__new__方法,如果many=True,生成ListSerializer对象,如果为False,生成Serializer对象
    3 序列化对象.data方法--调用父类data方法---调用对象自己的to_representation(自定义的序列化类无此方法,去父类找)
    4 Aerializer类里有to_representation方法,for循环执行attribute = field.get_attribute(instance)
    5 再去Field类里去找get_attribute方法,self.source_attrs就是被切分的source,然后执行get_attribute方法,source_attrs
    6 当参数传过去,判断是方法就加括号执行,是属性就把值取出来
    7 '''

        图书的增删查改resful接口:

          
     1 class BookSerializers(serializers.ModelSerializer):
     2     class Meta:
     3         model=models.Book
     4         fields='__all__'
     5 
     6 
     7 class BookView(APIView):
     8 
     9     def get(self, request):
    10         book_list = models.Book.objects.all()
    11         bs = BookSerializers(book_list, many=True)
    12         # 序列化数据
    13 
    14         return Response(bs.data)
    15 
    16     def post(self, request):
    17         # 添加一条数据
    18         print(request.data)
    19 
    20         bs=BookSerializers(data=request.data)
    21         if bs.is_valid():
    22             bs.save()  # 生成记录
    23             return Response(bs.data)
    24         else:
    25 
    26             return Response(bs.errors)
    27 
    28 class BookDetailView(APIView):
    29     def get(self,request,pk):
    30         book_obj=models.Book.objects.filter(pk=pk).first()
    31         bs=BookSerializers(book_obj,many=False)
    32         return Response(bs.data)
    33     def put(self,request,pk):
    34         book_obj = models.Book.objects.filter(pk=pk).first()
    35 
    36         bs=BookSerializers(data=request.data,instance=book_obj)
    37         if bs.is_valid():
    38             bs.save() # update
    39             return Response(bs.data)
    40         else:
    41             return Response(bs.errors)
    42     def delete(self,request,pk):
    43         models.Book.objects.filter(pk=pk).delete()
    44 
    45         return Response("")
    views.py
          
    1     url(r'^books/$', views.BookView.as_view()),
    2     url(r'^books/(?P<pk>d+)$', views.BookDetailView.as_view()),
    urls.py

     四、认证组件

    • 认证简介

          只有认证通过的用户才能访问指定的url地址,比如:查询课程信息,需要登录之后才能查看,没有登录,就不能查看,这时候需要用到认证组件

    • 局部使用

        
    1 class User(models.Model):
    2     username=models.CharField(max_length=32)
    3     password=models.CharField(max_length=32)
    4     user_type=models.IntegerField(choices=((1,'超级用户'),(2,'普通用户'),(3,'二笔用户')))
    5 
    6 class UserToken(models.Model):
    7     user=models.OneToOneField(to='User')
    8     token=models.CharField(max_length=64)
    models.py
        
     1 from rest_framework.authentication import BaseAuthentication
     2 class TokenAuth():
     3     def authenticate(self, request):
     4         token = request.GET.get('token')
     5         token_obj = models.UserToken.objects.filter(token=token).first()
     6         if token_obj:
     7             return
     8         else:
     9             raise AuthenticationFailed('认证失败')
    10     def authenticate_header(self,request):
    11         pass
    新建认证类(验证通过return两个参数)
        
     1 def get_random(name):
     2     import hashlib
     3     import time
     4     md=hashlib.md5()
     5     md.update(bytes(str(time.time()),encoding='utf-8'))
     6     md.update(bytes(name,encoding='utf-8'))
     7     return md.hexdigest()
     8 class Login(APIView):
     9     def post(self,reuquest):
    10         back_msg={'status':1001,'msg':None}
    11         try:
    12             name=reuquest.data.get('name')
    13             pwd=reuquest.data.get('pwd')
    14             user=models.User.objects.filter(username=name,password=pwd).first()
    15             if user:
    16                 token=get_random(name)
    17                 models.UserToken.objects.update_or_create(user=user,defaults={'token':token})
    18                 back_msg['status']='1000'
    19                 back_msg['msg']='登录成功'
    20                 back_msg['token']=token
    21             else:
    22                 back_msg['msg'] = '用户名或密码错误'
    23         except Exception as e:
    24             back_msg['msg']=str(e)
    25         return Response(back_msg)
    26 
    27 
    28 
    29 class Course(APIView):
    30     authentication_classes = [TokenAuth, ]
    31 
    32     def get(self, request):
    33         return HttpResponse('get')
    34 
    35     def post(self, request):
    36         return HttpResponse('post')
    views.py
        
     1 def get_token(id,salt='123'):
     2     import hashlib
     3     md=hashlib.md5()
     4     md.update(bytes(str(id),encoding='utf-8'))
     5     md.update(bytes(salt,encoding='utf-8'))
     6 
     7     return md.hexdigest()+'|'+str(id)
     8 
     9 def check_token(token,salt='123'):
    10     ll=token.split('|')
    11     import hashlib
    12     md=hashlib.md5()
    13     md.update(bytes(ll[-1],encoding='utf-8'))
    14     md.update(bytes(salt,encoding='utf-8'))
    15     if ll[0]==md.hexdigest():
    16         return True
    17     else:
    18         return False
    19 
    20 class TokenAuth():
    21     def authenticate(self, request):
    22         token = request.GET.get('token')
    23         success=check_token(token)
    24         if success:
    25             return
    26         else:
    27             raise AuthenticationFailed('认证失败')
    28     def authenticate_header(self,request):
    29         pass
    30 class Login(APIView):
    31     def post(self,reuquest):
    32         back_msg={'status':1001,'msg':None}
    33         try:
    34             name=reuquest.data.get('name')
    35             pwd=reuquest.data.get('pwd')
    36             user=models.User.objects.filter(username=name,password=pwd).first()
    37             if user:
    38                 token=get_token(user.pk)
    39                 # models.UserToken.objects.update_or_create(user=user,defaults={'token':token})
    40                 back_msg['status']='1000'
    41                 back_msg['msg']='登录成功'
    42                 back_msg['token']=token
    43             else:
    44                 back_msg['msg'] = '用户名或密码错误'
    45         except Exception as e:
    46             back_msg['msg']=str(e)
    47         return Response(back_msg)
    48 from rest_framework.authentication import BaseAuthentication
    49 class TokenAuth():
    50     def authenticate(self, request):
    51         token = request.GET.get('token')
    52         token_obj = models.UserToken.objects.filter(token=token).first()
    53         if token_obj:
    54             return
    55         else:
    56             raise AuthenticationFailed('认证失败')
    57     def authenticate_header(self,request):
    58         pass
    59 
    60 class Course(APIView):
    61     authentication_classes = [TokenAuth, ]
    62 
    63     def get(self, request):
    64         return HttpResponse('get')
    65 
    66     def post(self, request):
    67         return HttpResponse('post')
    不存数据库的token验证

        总结:局部使用,只需要在视图类里加入:

         authentication_classes = [TokenAuth, ] 

    • 全局使用

    REST_FRAMEWORK={
        "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",]
    }
    • 源码分析

     1 #Request对象的user方法
     2 @property
     3 def user(self):
     4 the authentication classes provided to the request.
     5         if not hasattr(self, '_user'):
     6             with wrap_attributeerrors():
     7                 self._authenticate()
     8         return self._user
     9 
    10 def _authenticate(self):
    11         for authenticator in self.authenticators:
    12             try:
    13                 user_auth_tuple = authenticator.authenticate(self)
    14             except exceptions.APIException:
    15                 self._not_authenticated()
    16                 raise
    17             #认证成功,可以返回一个元组,但必须是最后一个验证类才能返回
    18             if user_auth_tuple is not None:
    19                 self._authenticator = authenticator
    20                 self.user, self.auth = user_auth_tuple
    21                 return
    22 
    23         self._not_authenticated()
    View Code

    self.authenticators

        def get_authenticators(self):
            return [auth() for auth in self.authentication_classes]

    认证类使用顺序:先用视图类中的验证类,再用settings里配置的验证类,最后用默认的验证类

    五、权限组件

    • 权限简介

          只用超级用户才能访问指定的数据,普通用户不能访问,所以就要有权限组件对其限制

    • 局部使用

        
     1 from rest_framework.permissions import BasePermission
     2 class UserPermission(BasePermission):
     3     message = '不是超级用户,查看不了'
     4     def has_permission(self, request, view):
     5         # user_type = request.user.get_user_type_display()
     6         # if user_type == '超级用户':
     7         user_type = request.user.user_type
     8         print(user_type)
     9         if user_type == 1:
    10             return True
    11         else:
    12             return False
    13 class Course(APIView):
    14     authentication_classes = [TokenAuth, ]
    15     permission_classes = [UserPermission,]
    16 
    17     def get(self, request):
    18         return HttpResponse('get')
    19 
    20     def post(self, request):
    21         return HttpResponse('post')
    View Code

        局部使用只需要在视图类里加入:

         permission_classes = [UserPermission,] 

    • 全局使用

    REST_FRAMEWORK={
        "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
        "DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",]
    }
    • 源码分析

    1 def check_permissions(self, request):
    2     for permission in self.get_permissions():
    3         if not permission.has_permission(request, self):
    4             self.permission_denied(
    5                 request, message=getattr(permission, 'message', None)
    6                 )
    View Code

    self.get_permissions()

    def get_permissions(self):
         return [permission() for permission in self.permission_classes]

    权限类使用顺序:先用视图类中的权限类,再用settings里配置的权限类,最后用默认的权限类

    六、频率组件

    • 频率简介

          为了控制用户对某个url请求的频率,比如,一分钟以内,只能访问三次

    • 自定义频率类,自定义频率规则

        自定义的逻辑:

    #(1)取出访问者ip
    # (2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
    # (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
    # (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
    # (5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败

        代码实现:

     
     1 class MyThrottles():
     2     VISIT_RECORD = {}
     3     def __init__(self):
     4         self.history=None
     5     def allow_request(self,request, view):
     6         #(1)取出访问者ip
     7         # print(request.META)
     8         ip=request.META.get('REMOTE_ADDR')
     9         import time
    10         ctime=time.time()
    11         # (2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问
    12         if ip not in self.VISIT_RECORD:
    13             self.VISIT_RECORD[ip]=[ctime,]
    14             return True
    15         self.history=self.VISIT_RECORD.get(ip)
    16         # (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
    17         while self.history and ctime-self.history[-1]>60:
    18             self.history.pop()
    19         # (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
    20         # (5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
    21         if len(self.history)<3:
    22             self.history.insert(0,ctime)
    23             return True
    24         else:
    25             return False
    26     def wait(self):
    27         import time
    28         ctime=time.time()
    29         return 60-(ctime-self.history[-1])
    View Code
    • 内置频率类及局部使用

          写一个类,继承自SimpleRateThrottle,(根据ip限制)问:要根据用户现在怎么写

    1 from rest_framework.throttling import SimpleRateThrottle
    2 class VisitThrottle(SimpleRateThrottle):
    3     scope = 'luffy'
    4     def get_cache_key(self, request, view):
    5         return self.get_ident(request)

          在setting里配置:(一分钟访问三次)

    1 REST_FRAMEWORK = {
    2     'DEFAULT_THROTTLE_RATES':{
    3         'luffy':'3/m'
    4     }
    5 }

          在视图类里使用

    throttle_classes = [MyThrottles,]

          错误信息的中文提示:

     1 class Course(APIView):
     2     authentication_classes = [TokenAuth, ]
     3     permission_classes = [UserPermission, ]
     4     throttle_classes = [MyThrottles,]
     5 
     6     def get(self, request):
     7         return HttpResponse('get')
     8 
     9     def post(self, request):
    10         return HttpResponse('post')
    11     def throttled(self, request, wait):
    12         from rest_framework.exceptions import Throttled
    13         class MyThrottled(Throttled):
    14             default_detail = '傻逼啊'
    15             extra_detail_singular = '还有 {wait} second.'
    16             extra_detail_plural = '出了 {wait} seconds.'
    17         raise MyThrottled(wait)
    View Code

          内置频率限制类:

        

        BaseThrottle是所有类的基类:方法:def get_ident(self, request)获取标识,其实就是获取ip,自定义的需要继承它

        AnonRateThrottle:未登录用户ip限制,需要配合auth模块用

        SimpleRateThrottle:重写此方法,可以实现频率现在,不需要咱们手写上面自定义的逻辑

        UserRateThrottle:登录用户频率限制,这个得配合auth模块来用

        ScopedRateThrottle:应用在局部视图上的(忽略)

    • 内置频率类及全局使用

    REST_FRAMEWORK = {
        'DEFAULT_THROTTLE_CLASSES':['app01.utils.VisitThrottle',],
        'DEFAULT_THROTTLE_RATES':{
            'luffy':'3/m'
        }
    }
    • 源码分析

        省略。。。。。。

    七、分页器组件

    • 简单分页(查看第n页,每页显示n条)

     1 from rest_framework.pagination import PageNumberPagination
     2 # 一 基本使用:url=url=http://127.0.0.1:8000/pager/?page=2&size=3,size无效
     3 class  Pager(APIView):
     4     def get(self,request,*args,**kwargs):
     5         # 获取所有数据
     6         ret=models.Book.objects.all()
     7         # 创建分页对象
     8         page=PageNumberPagination()
     9         # 在数据库中获取分页的数据
    10         page_list=page.paginate_queryset(ret,request,view=self)
    11         # 对分页进行序列化
    12         ser=BookSerializer1(instance=page_list,many=True)
    13         return Response(ser.data)
    14 # 二 自定制 url=http://127.0.0.1:8000/pager/?page=2&size=3
    15 # size=30,无效,最多5条
    16 class Mypage(PageNumberPagination):
    17     page_size = 2
    18     page_query_param = 'page'
    19     # 定制传参
    20     page_size_query_param = 'size'
    21     # 最大一页的数据
    22     max_page_size = 5
    23 class  Pager(APIView):
    24     def get(self,request,*args,**kwargs):
    25         # 获取所有数据
    26         ret=models.Book.objects.all()
    27         # 创建分页对象
    28         page=Mypage()
    29         # 在数据库中获取分页的数据
    30         page_list=page.paginate_queryset(ret,request,view=self)
    31         # 对分页进行序列化
    32         ser=BookSerializer1(instance=page_list,many=True)
    33         # return Response(ser.data)
    34         # 这个也是返回Response对象,但是比基本的多了上一页,下一页,和总数据条数(了解即可)
    35         return page.get_paginated_response(ser.data)

    setting中配置:

    REST_FRAMEWORK = {
        # 每页显示两条
        'PAGE_SIZE':2
    }

     路由: url(r'^pager/$', views.Pager.as_view()), 

    新建类: Serializers

    1 class BookSerializer1(serializers.ModelSerializer):
    2     class Meta:
    3         model=models.Book
    4         # fields="__all__"
    5         exclude=('authors',)
    • 偏移分页(在第n个位置,向后查看n条数据)

     1 # http://127.0.0.1:8000/pager/?offset=4&limit=3
     2 from rest_framework.pagination import LimitOffsetPagination
     3 # 也可以自定制,同简单分页
     4 class  Pager(APIView):
     5     def get(self,request,*args,**kwargs):
     6         # 获取所有数据
     7         ret=models.Book.objects.all()
     8         # 创建分页对象
     9         page=LimitOffsetPagination()
    10         # 在数据库中获取分页的数据
    11         page_list=page.paginate_queryset(ret,request,view=self)
    12         # 对分页进行序列化
    13         ser=BookSerializer1(instance=page_list,many=True)
    14         # return page.get_paginated_response(ser.data)
    15         return Response(ser.data)
    • CursorPagination(加密分页,只能看上一页和下一页,速度快)

     1 from rest_framework.pagination import CursorPagination
     2 # 看源码,是通过sql查询,大于id和小于id
     3 class  Pager(APIView):
     4     def get(self,request,*args,**kwargs):
     5         # 获取所有数据
     6         ret=models.Book.objects.all()
     7         # 创建分页对象
     8         page=CursorPagination()
     9         page.ordering='nid'
    10         # 在数据库中获取分页的数据
    11         page_list=page.paginate_queryset(ret,request,view=self)
    12         # 对分页进行序列化
    13         ser=BookSerializer1(instance=page_list,many=True)
    14         # 可以避免页码被猜到
    15         return page.get_paginated_response(ser.data)
  • 相关阅读:
    go语言基础之map赋值、遍历、删除 、做函数参数
    go语言基础之map介绍和使用
    go语言基础之go猜数字游戏
    go语言基础之切片做函数参数
    ORA-28001: the password has expired解决方法
    windows10下设置Maven的本地仓库和阿里云的远程中央仓库
    mvn安装
    elk基于jolokia监控springboot应用jvm方案
    陌陌风控系统
    ElasticSearch SIEM方案
  • 原文地址:https://www.cnblogs.com/child-king/p/10456641.html
Copyright © 2011-2022 走看看