目录导航
一、RESTful 规范
二、APIView 组件
三、序列化组件
四、认证组件
五、权限组件
六、频率组件
七、分页器组件
一、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)
二、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)
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
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)
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)
三、序列化组件
-
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
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))
注意:
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')
-
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=Bookfields="__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("")
1 url(r'^books/$', views.BookView.as_view()), 2 url(r'^books/(?P<pk>\d+)$', views.BookDetailView.as_view()),
四、认证组件
-
认证简介
只有认证通过的用户才能访问指定的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)
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
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')
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')
总结:局部使用,只需要在视图类里加入:
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()
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')
局部使用只需要在视图类里加入:
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 )
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])
-
内置频率类及局部使用
写一个类,继承自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)
内置频率限制类:
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)