限制
DRF内置了基本的限制类,首先我们自己动手写一个限制类,熟悉下限制组件的执行过程。
自定义限制类
import time visit_record = {} class MyThrottle(object): def __init__(self): self.history = None def allow_request(self, request, view): # 拿到当前的请求的ip作为访问记录的 key ip = request.META.get('REMOTE_ADDR') # 拿到当前请求的时间戳 now = time.time() if ip not in visit_record: visit_record[ip] = [] # 把当前请求的访问记录拿出来保存到一个变量中 history = visit_record[ip] self.history = history # 循环访问历史,把超过10秒钟的请求时间去掉 while history and now - history[-1] > 10: history.pop() # 此时 history中只保存了最近10秒钟的访问记录 if len(history) >= 3: return False else: # 判断之前有没有访问记录(第一次来) self.history.insert(0, now) return True def wait(self): """告诉客户端还需等待多久""" now = time.time() return self.history[-1] + 10 - now # history = ['9:56:12', '9:56:10', '9:56:09', '9:56:08'] # '9:56:18' - '9:56:12' # history = ['9:56:19', '9:56:18', '9:56:17', '9:56:08'] # 最后一项到期的时间就是下一次允许请求的时间 # 最后一项到期的时间:now - history[-1] > 10 # 最后一项还剩多少时间过期 # history[-1] + 10 - now
视图使用
from auth_demo.throttle import MyThrottle # 登录之后才能看到数据接口 class TestAuthView(APIView): throttle_classes = [MyThrottle, ]#视图局部使用 def get(self, request): return Response('这个视图里面的数据只有登录后才能看到!')
全局使用
# 在settings.py中设置rest framework相关配置项 REST_FRAMEWORK = { "DEFAULT_THROTTLE_CLASSES": ["auth_demo.throttle.MyThrottle", ] }
使用内置限制类
from rest_framework.throttling import SimpleRateThrottle class VisitThrottle(SimpleRateThrottle): scope = "xxx" def get_cache_key(self, request, view): return self.get_ident(request)
全局配置
REST_FRAMEWORK = { "DEFAULT_THROTTLE_CLASSES": ["auth_demo.throttle.VisitThrottle", ], "DEFAULT_THROTTLE_RATES": { "xxx": "5/m", }}
局部配置
from auth_demo.throttle import VisitThrottle # 登录之后才能看到数据接口 class TestAuthView(APIView): throttle_classes = [VisitThrottle, ] def get(self, request): return Response('这个视图里面的数据只有登录后才能看到!')
DRF分页组件
为什么要使用分页
我们数据表中可能会有成千上万条数据,当我们访问某张表的所有数据时,我们不太可能需要一次把所有的数据都展示出来,因为数据量很大,对服务端的内存压力比较大还有就是网络传输过程中耗时也会比较大。
通常我们会希望一部分一部分去请求数据,也就是我们常说的一页一页获取数据并展示出来。
DRF使用分页器
分页模式
rest framework中提供了三种分页模式:
PageNumberPagination 按照页码
LimitOffsetPagination 数据库查询 适合数据量庞大的
CursorPagination 加密
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
全局配置
REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', 'PAGE_SIZE': 100#每页的数据个数 }
局部配置
我们可以在视图类中进行局部设置
class PublisherViewSet(ModelViewSet): queryset = models.Publisher.objects.all() serializer_class = PublisherModelSerializer pagination_class = PageNumberPagination # 注意不是列表(只能有一个分页模式)
DRF内置分页器
PageNumberPagination
按页码数分页,第n页,每页显示m条数据
例如:http://127.0.0.1:8000/api/article/?page=2&size=1
分页器
class MyPageNumber(PageNumberPagination): page_size = 2 # 每页显示多少条 page_size_query_param = 'size' # URL中每页显示条数的参数 page_query_param = 'page' # URL中页码的参数 page_size页尺寸 max_page_size = None # 最大页码数限制
视图
class ArticleList(APIView): def get(self, request, *args, **kwargs): res = {"code": 0} article_list = models.Article.objects.all().order_by("id") # 分页 page_obj = MyPageNumber() page_article = page_obj.paginate_queryset(queryset=article_list, request=request, view=self) ser_obj = ArticleSerializer(page_article, many=True) res["data"] = ser_obj.data return Response(res)
返回带页码链接的响应
class ArticleList(APIView): def get(self, request, *args, **kwargs): res = {"code": 0} article_list = models.Article.objects.all().order_by("id") # 分页 page_obj = MyPageNumber() page_article = page_obj.paginate_queryset(queryset=article_list, request=request, view=self) ser_obj = ArticleSerializer(page_article, many=True) res["data"] = ser_obj.data return page_obj.get_paginated_response(res)
LimitOffsetPagination
分页,在n位置,向后查看m条数据
例如:http://127.0.0.1:8000/api/article/?offset=2&limit=2
分页器
class MyPageNumber(PageNumberPagination): page_size = 2 # 每页显示多少条 page_size_query_param = 'size' # URL中每页显示条数的参数 page_query_param = 'page' # URL中页码的参数 max_page_size = None # 最大页码数限制
CursorPagination
加密分页,把上一页和下一页的id值记住
# 加密分页 class MyCursorPagination(CursorPagination): cursor_query_param = 'cursor' page_size = 1 ordering = '-id' # 重写要排序的字段
order_by('-id') 排序
解析器
解析器的作用
解析器的作用就是服务端接收客户端传过来的数据,把数据解析成自己可以处理的数据。本质就是对请求体中的数据进行解析。
在了解解析器之前,我们要先知道Accept以及ContentType请求头。
Accept是告诉对方我能解析什么样的数据,通常也可以表示我想要什么样的数据。
ContentType是告诉对方我给你的是什么样的数据类型。
解析器工作原理的就是拿到请求的ContentType来判断前端给我的数据类型是什么,然后我们在后端使用相应的解析器去解析数据。
Django中的数据解析
在视图中我们可以通过request.POST来获取前端发来的请求数据,那么Django框架是如何拿到请求体中的数据的呢?我们一起来看一下:
我们可以在单个视图或者全局的settings.py中配置要使用的解析器。
单个视图配置
class BookViewSet(ModelViewSet): queryset = models.Book.objects.all() serializer_class = BookModelSerializer parser_classes = [JSONParser, ]
全局配置
REST_FRAMEWORK = { 'DEFAULT_PARSER_CLASSES': ( 'rest_framework.parsers.JSONParser', ) }
注意:当你的项目中只配置了 JSONParser 解析器时,你的项目现在就只能解析JSON格式的数据了,客户端如果使用浏览器提交,那么你将无法解析。
注意,在视图类中定义的配置项的优先级要高于全局配置中的配置项。
渲染器
渲染器同解析器相反,它定义了框架按照content_type来返回不同的响应。
DRF提供的渲染器有很多,默认是
'DEFAULT_RENDERER_CLASSES': ( # 渲染器 # 你要页面 我就给你页面 # 你要JSON格式的数据 我就给你JSON格式的数据 'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.JSONRenderer', 'rest_framework.renderers.BrowsableAPIRenderer', ),
我们也可以在视图中局部设置也可以在全局的settings.py中进行设置:
局部设置
class PublisherViewSet(ModelViewSet): queryset = models.Publisher.objects.all() serializer_class = PublisherModelSerializer renderer_classes = [JSONRenderer, ]
这样设置后就只能返回JSON格式的数据了,并不会像之前一样提供一个阅读友好的web页面。
全局设置
REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.JSONRenderer', ), }