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)

 

 

posted @ 2019-03-01 16:46  时光毁了少年梦  阅读(361)  评论(0编辑  收藏  举报