restful(3):认证、权限、频率 & 解析器、路由控制、分页、渲染器、版本
models.py中:
class UserInfo(models.Model):
name = models.CharField(max_length=32)
psw = models.CharField(max_length=32)
user_type_choices = ((1,"普通"),(2,"VIP"),(3,"SVIP"))
user_type = models.SmallIntegerField(choices=user_type_choices,default=1) # 新添加一个标识用户权限级别的字段 class Token(models.Model): # Token类用于 认证
user =
models.OneToOneField(to="UserInfo",on_delete=models.CASCADE)
token = models.CharField(max_length=128)
认证、权限和频率
# 认证类中一定要有一个 authenticate() 的方法
# 权限类中一定要有一个 has_permission() 的方法 (认证组件执行时会 request.user = 当前登陆用户)
# 频率类中一定要有一个 allow_request() 的方法 # 执行组件:认证、权限、频率
# 认证:request.user
self.initial(request,*args,**kwargs):
==== # 认证组件
self.perform_authentication(request)
# 权限组件
self.check_permissions(request)
# 频率组件
self.check_throttles(request)
### 这三个组件也是在dispatch()执行的时候执行(有访问请求的时候) request.META.get("REMOTE_ADDR") # 客户端的IP地址
认证组件:
局部视图认证:
在app01.service.auth.py:
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed class Authentication(BaseAuthentication): def authenticate(self,request): # authenticate() 这个方法名是固定的 # http:www...../?token=soihtfn7a9sdfvb987... # url中应该带有token
# token=request._request.GET.get("token")
token = request.query_params.get("token") # request.query_params # 获取到 GET请求数据(/? 后面的数据,和请求方式无关;POST请求时,也能 request.GET来获取 /? 后面的数据);request是后来封装好的request
token_obj=UserToken.objects.filter(token=token).first()
if not token_obj: # 检查token是否存在
raise AuthenticationFailed("验证失败!") # 认证失败时的固定语法
return (token_obj.user,token_obj) # 认证成功后需要返回一个元组:第一个是用户有关的信息,第二个参数是token对象
在views.py:
def get_random_str(user):
import hashlib,time
ctime=str(time.time()) md5=hashlib.md5(bytes(user,encoding="utf8")) # user是为了“加盐”处理
md5.update(bytes(ctime,encoding="utf8")) return md5.hexdigest() from app01.service.auth import * from django.http import JsonResponse
class LoginViewSet(APIView):
# authentication_classes = [Authentication,] # authentication_classes 是固定写法;需要认证的类都写在后面的列表中
def post(self,request,*args,**kwargs):
res={"code":1000,"msg":None}
try:
user=request._request.POST.get("user")
pwd=request._request.POST.get("pwd")
user_obj=UserInfo.objects.filter(user=user,pwd=pwd).first()
print(user,pwd,user_obj)
if not user_obj:
res["code"]=1001
res["msg"]="用户名或者密码错误"
else:
token=get_random_str(user)
UserToken.objects.update_or_create(user=user_obj,defaults={"token":token}) # 表中没有就创建,有就更新;# defaults表示:除了defaults 中的字段外,其它的字段联合比较是否已经存在,存在则更新,不存在则创建 # 返回一个元组:第一个是对象,第二个是布尔值(表示create还是update)
res["token"]=token except Exception as e:
res["code"]=1002
res["msg"]=e return JsonResponse(res,json_dumps_params={"ensure_ascii":False}) # {"ensure_ascii":False} 作用:显示中文
全局视图认证组件:
settings.py配置如下:
REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",] # 写上 认证类的路径
}
认证源码:
self表示封装之后的request,所以,认证完成之后,request.user 和 request.auth 就是你赋给它们的值
自定义用户认证的类:
# 自定义用户验证的类(如手机或邮箱配合密码登陆;因为默认是 用户名和密码验证)需要继承 ModelBackend;并且需要在 settings 中设置 AUTHENTICATION_BACKENDS = ("路径.自定义用户验证的类",)
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
class CustomBackend(ModelBackend):
def authenticate(self,username=None,password=None,**kwargs):
try:
user = models.User.objects.get(Q(username=username)|Q(email=username)|Q(mobile=username))
if user.check_password(password): # 前端传过来的密码是明文的,Django中保存的是密文,check_password() 会把明文转化为密文
return user
except Exception as e:
return None
权限组件
局部视图权限
在app01.service.permissions.py中:
from rest_framework.permissions import BasePermission
class SVIPPermission(BasePermission):
message="SVIP才能访问!" # 没有权限时返回的错误信息
def has_permission(self, request, view):
if request.user.user_type==3:
return True
return False # return True就是通过权限认证, return False 即没有权限
在views.py:
from app01.service.permissions import * class BookViewSet(generics.ListCreateAPIView):
permission_classes = [SVIPPermission,] # permission_classes 是固定写法;需要校验的权限类都写在后面的列表中(这是局部权限校验)
queryset = Book.objects.all()
serializer_class = BookSerializers
全局视图权限:
settings.py配置如下:
REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
"DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",] # 写上权限认证类的路径
}
登陆权限的类:IsAuthenticated
from rest_framework.permissions import IsAuthenticated class xxxViewSet():
permission_classes = (IsAuthenticated,) # 该视图只有登陆后才能访问
只有对象的拥有者才能有权限操作(如:只能删除自己的收藏):
# 只有对象的拥有者才能有权限操作(如:只能删除自己的收藏) class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Object-level permission to only allow owners of an object to edit it.
Assumes the model instance has an `owner` attribute.
""" def has_object_permission(self, request, view, obj): # obj 表示被操作的对象,如一条收藏记录
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True # Instance must have an attribute named `owner`.
return obj.owner == request.user
只能查看自己的内容(GET请求;)
class UserFavViewSet(mixin.CreateModelMixin,mixin.ListModelMixin,mixin.RetrieveMixin,generics.GenericAPIView):
authentication_classes = (JSONWebTokenAuthentication,SessionAuthentication) # ?? 此处有疑问
permission_classes = (IsAuthenticated,IsOwnerReadOnly)
serializer_class = UserFavSerializer
lookup_field = "goods_id" # 用于执行各个model实例的对象查找的model字段,默认是 "pk"; 程序先 执行的 get_queryset() ,然后才走的 这一步 def get_queryset(self): # 有了 get_queryset() 这个方法时, 上面就不再需要写 queryset
return UserFav.objects.filter(user=self.request.user) # 筛选出 user 为当前登陆用户的 记录 (即 只能查看自己的)
throttle(访问频率)组件
局部视图throttle
在app01.service.throttles.py中:
from rest_framework.throttling import BaseThrottle VISIT_RECORD={}
class VisitThrottle(BaseThrottle): def __init__(self):
self.history=None def allow_request(self,request,view): # allow_request()是固定的方法名 # 以下为业务逻辑(rest 只处理数据,不处理逻辑)
remote_addr = request.META.get('REMOTE_ADDR') # 客户端的IP地址
print(remote_addr)
import time
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 history[-1]<ctime-60:
history.pop() if len(history)<3:
history.insert(0,ctime)
return True # return True 表示通过验证
else:
return False # return False 表示没通过验证 def wait(self):
import time
ctime=time.time()
return 60-(ctime-self.history[-1])
在views.py中:
from app01.service.throttles import * class BookViewSet(generics.ListCreateAPIView):
throttle_classes = [VisitThrottle,] # throttle_classes 是固定写法;
queryset = Book.objects.all()
serializer_class = BookSerializers
全局视图throttle
REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
"DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",],
"DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",]
}
内置throttle类
在app01.service.throttles.py修改为:
class VisitThrottle(SimpleRateThrottle): scope="visit_rate"
def get_cache_key(self, request, view): return self.get_ident(request)
settings.py设置:
REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
"DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",],
"DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",],
"DEFAULT_THROTTLE_RATES":{
"visit_rate":"5/m",
}
}
使用默认的Throttling:
1. 配置settings
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
),
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day',
'user': '1000/day'
}
}
2. 在相关视图中添加 throttle_classes 的类,如:
from rest_framework.throttling import AnonRateThrottle,UserRateThrottle class GoodsViewSet(mixins.ListModelMixin,mixins.RetrieveModelMixin,GenericViewSet):
"""
商品列表页,分页,过滤,搜索,排序
list:
所有商品列表
retrieve:
查看单个商品
"""
queryset = Goods.objects.all().order_by("pk")
serializer_class = GoodsSerializer
pagination_class = GoodsPagination
throttle_classes = (AnonRateThrottle,UserRateThrottle) # 控制频率的类
filter_backends = (DjangoFilterBackend,filters.SearchFilter,filters.OrderingFilter)
filter_class = GoodsFilter # 过滤
search_fields = ("name","goods_brief","goods_details") # 搜索
ordering_fields = ("sold_num","shop_price") # 排序 # 修改点击数
def retrieve(self, request, *args, **kwargs):
instance = self.get_object() # instance 是一个 Goods() 的对象
instance.click_num += 1 # 点击数 +1
instance.save()
serializer = self.get_serializer(instance)
return Response(serializer.data)
解析器
局部视图
from rest_framework.parsers import JSONParser,FormParser
class PublishViewSet(generics.ListCreateAPIView):
parser_classes = [FormParser,JSONParser] # parser_classes 是固定写法;解析器名放在后面的列表中
queryset = Publish.objects.all()
serializer_class = PublshSerializers
def post(self, request, *args, **kwargs):
print("request.data",request.data)
return self.create(request, *args, **kwargs)
全局视图
REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
"DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",],
"DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",],
"DEFAULT_THROTTLE_RATES":{
"visit_rate":"5/m",
},
"DEFAULT_PARSER_CLASSES":['rest_framework.parsers.FormParser',]
}
路由控制
路由控制针对的只是以下这种情况:
# urls.py部分:
re_path(r"^books/$",views.BookModelView.as_view({"get":"list","post":"create"})),
re_path(r"^books/(?P<pk>\d+)/$",views.BookModelView.as_view({"get":"retrieve","put":"update","delete":"destroy"})), # views.py部分:
class BookModelView(viewsets.ModelViewSet):
queryset = models.Book.objects.all() # queryset 表示要处理的数据;queryset这个变量名是固定的
serializer_class = serializer.BookSerializers # serializer_class 表示 所要用到的 序列化的类;serializer_class 是固定写法
上面的两条 url 可以利用 路由控制 组件来简化:
# urls.py 中 from rest_framework import routers
from django.urls import path,re_path,include
from app01 import views routers = routers.DefaultRouter()
routers.register("books",views.BookModelView) # 第一个参数是路径的前缀,第二参数是 视图类 名称 # 这两行代码执行后,会生成四条 以 books/ 为前缀的 url urlpatterns = [
re_path(r'^', include(routers.urls)), # 把上面 register() 的路径在 urlpatterns 中 include 一下
]
分页
普通分页
from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination class PNPagination(PageNumberPagination):
page_size = 1 # 后端设置的每页条数
page_query_param = 'page' # 前端查询页码的参数
page_size_query_param = "size" # 前端临时修改每页条数的参数
max_page_size = 5 # 前端能修改的每页条数 的最大值 class BookViewSet(viewsets.ModelViewSet): queryset = Book.objects.all()
serializer_class = BookSerializers # 继承 ModelViewSet 时 需要修改其 list() 方法
def list(self,request,*args,**kwargs): book_list=Book.objects.all()
pp=PNPagination()
pager_books=pp.paginate_queryset(queryset=book_list,request=request,view=self) # 分页函数
print(pager_books)
bs=BookSerializers(pager_books,many=True) return Response(bs.data)
# return pp.get_paginated_response(bs.data)
偏移分页
from rest_framework.pagination import LimitOffsetPagination
响应器:
from rest_framework.response import Response # Response 内部会自动做序列化
渲染器:
渲染器作用:规定页面显示的效果(无用)
局部渲染:
from rest_framework.renderers import JSONRenderer class TestView(APIView):
renderer_classes = [JSONRenderer, ] # renderer_classes 渲染器固定写法; 通常用 都用 JSONRenderer--- 只渲染为 Json字符串 def get(self, request, *args, **kwargs):
user_list = models.UserInfo.objects.all()
ser = TestSerializer(instance=user_list, many=True)
return Response(ser.data)
全局渲染配置:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES':['rest_framework.renderers.JSONRenderer',]
}
版本
a. 基于url的get传参方式: 如:/users?version=v1
settings 中的配置:
REST_FRAMEWORK = {
'DEFAULT_VERSION': 'v1', # 默认版本
'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本
'VERSION_PARAM': 'version' # URL中获取值的key
}
from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'^test/', TestView.as_view(),name='test'),
] urls.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning class TestView(APIView):
versioning_class = QueryParameterVersioning def get(self, request, *args, **kwargs): # 获取版本
print(request.version)
# 获取版本管理的类
print(request.versioning_scheme) # 反向生成URL
reverse_url = request.versioning_scheme.reverse('test', request=request)
print(reverse_url) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容') views.py
b. 基于url的正则方式:
如:/v1/users/
REST_FRAMEWORK = {
'DEFAULT_VERSION': 'v1', # 默认版本
'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本
'VERSION_PARAM': 'version' # URL中获取值的key
}
from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'^(?P<version>[v1|v2]+)/test/', TestView.as_view(), name='test'),
] urls.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import URLPathVersioning class TestView(APIView):
versioning_class = URLPathVersioning def get(self, request, *args, **kwargs):
# 获取版本: request.version
print(request.version)
# 获取版本管理的类
print(request.versioning_scheme) # 反向生成URL
reverse_url = request.versioning_scheme.reverse('test', request=request)
print(reverse_url) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容') # views.py
全局使用:
REST_FRAMEWORK = {
'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning",
'DEFAULT_VERSION': 'v1',
'ALLOWED_VERSIONS': ['v1', 'v2'],
'VERSION_PARAM': 'version'
} # settings.py
restful(3):认证、权限、频率 & 解析器、路由控制、分页、渲染器、版本的更多相关文章
- Django_rest_framework_渲染器/解析器/路由控制/分页
目录 渲染器 解析器 路由控制 分页 渲染器 简介 什么是渲染器 根据 用户请求URL 或 用户可接受的类型,筛选出合适的 渲染组件. 渲染器的作用 序列化.友好的展示数据 渲染器配置 首先要在set ...
- restful知识点之三restframework认证-->权限-->频率
认证.权限.频率是层层递进的关系 权限业务时认证+权限 频率业务时:认证+权限+频率 局部认证方式 from django.conf.urls import url,include from djan ...
- 实战-DRF快速写接口(认证权限频率)
实战-DRF快速写接口 开发环境 Python3.6 Pycharm专业版2021.2.3 Sqlite3 Django 2.2 djangorestframework3.13 测试工具 Postma ...
- Django REST Framework - 分页 - 渲染器 - 解析器
为什么要使用分页? 我们数据表中可能会有成千上万条数据,当我们访问某张表的所有数据时,我们不太可能需要一次把所有的数据都展示出来,因为数据量很大,对服务端的内存压力比较大还有就是网络传输过程中耗时也会 ...
- 8) drf 三大认证 认证 权限 频率
一.三大认证功能分析 1)APIView的 dispath(self, request, *args, **kwargs) 2)dispath方法内 self.initial(request, *ar ...
- rest framework 认证 权限 频率
认证组件 发生位置 APIview 类种的 dispatch 方法执行到 initial 方法 进行 认证组件认证 源码位置 rest_framework.authentication 源码内部需要 ...
- DRF路由组件和渲染器组件
参考博客:https://www.cnblogs.com/wupeiqi/articles/7805382.html
- DRF-认证权限频率
目录 DRF-认证权限频率 认证 登录接口 认证 权限 作用 使用 频率 作用 使用 认证权限频率+五个接口 模型 视图 序列化器 认证权限频率类 配置文件 路由 DRF-认证权限频率 前后端混合开发 ...
- DRF-认证 权限 频率组件
补充 1 认证 权限 频率组件原理基本相同 2 认证相关: session cookie token 认证相关的 这里用token token 1 有时间限制,超时则失效 2 每次登录更换一个tok ...
- Django REST framework解析器和渲染器
解析器 解析器的作用 解析器的作用就是服务端接收客户端传过来的数据,把数据解析成自己可以处理的数据.本质就是对请求体中的数据进行解析. 在了解解析器之前,我们要先知道Accept以及ContentTy ...
随机推荐
- orcl 11g 创建表空间
Oracle11g创建表空间语句 在plsql工具中执行以下语句,可建立Oracle表空间. /*分为四步 *//*第1步:创建临时表空间 */create temporary tablespa ...
- jmeter(十五)Jmeter默认报告优化
一.本文目的: 之前写了两篇文章搭建持续集成接口测试平台(Jenkins+Ant+Jmeter)和ANT批量执行Jmeter脚本,功能实现上都没有什么问题,但是最后生成的报告有一点小问题,虽然不影响使 ...
- JS中的对象之原型
对象 ECMAScript做为一个高度抽象的面向对象语言,是通过_对象_来交互的.即使ECMAScript里边也有_基本类型_,但是,当需要的时候,它们也会被转换成对象. 一个对象就是一个属性集合,并 ...
- 程序员的职业方向: 是-->技术?还是-->管理?
岁之后还能不能再做程序员....... 绝大多数程序员最终的职业目标可能都是CTO,但能做到CEO的人估计会比较少,也有一少部分人自己去创业去当老板,也有部分人转行了,当老板的人毕竟是少数,转行的人都 ...
- qt查找框设置
转载请注明出处:http://www.cnblogs.com/dachen408/p/7229129.html 主界面弹出查找框方法,查找框显示在主界面上层,并还可以点击主界面,非模态. class ...
- python文件的读写的模式
<1>打开文件 在python,使用open函数,可以打开一个已经存在的文件,或者创建一个新文件 open(文件名,访问模式) 示例如下: f = open('test.txt', 'w' ...
- day1 python 基础
# 一行注释"""多行注释"""print("hello world\n" * 3)name = "sure& ...
- Flask框架 之上下文、请求钩子与Flask_Script
一.上下文 请求上下文:request与session 应用上下文:current_app与g:一次请求多个函数可以用它传参 @app.route("/") def index() ...
- 使用WinPcap编程
创建一个使用 wpcap.dll 的应用程序 用 Microsoft Visual C++ 创建一个使用 wpcap.dll 的应用程序,需要按一下步骤: 在每一个使用了库的源程序中,将 pcap.h ...
- chomp成功的返回值是1,chomp对参数去回车符后会改变参数的值,是传入又是传出参数。$arrow_notation = ( chomp( $unpackeing = <STDIN>) );
44 my $unpackeing; 45 my $arrow_notation = ''; 46 print "Enter name to query, enter ex ...