drf 认证组件、权限组件、频率组件
认证组件
访问某个接口 需要登陆后才能访问
#第一步 写一个登录功能 用户表
User表
UserToken表 :存储用户登录状态 【这个表可以没有 如果没有 把字段直接卸载User表上也可以】
登录接口
model.py
class Books(models.Model):
name = models.CharField(max_length=32)
price = models.CharField(max_length=32)
publish = models.ForeignKey(to="Publish",on_delete=models.CASCADE)
authors = models.ManyToManyField(to="Authors")
def publish_dict(self):
return {"name":self.publish.name,"addr":self.publish.addr}
def author_list(self):
l = []
for author in self.authors.all():
l.append({"name":author.name,"phone":author.phone})
return l
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
class Authors(models.Model):
name = models.CharField(max_length=32)
phone = models.CharField(max_length=32)
class User(models.Model):
username = models.CharField(max_length=32)
password = models.IntegerField()
class UserToken(models.Model):
token = models.CharField(max_length=32)
user = models.OneToOneField(to="User",on_delete=models.CASCADE,null=True)
url.py
router = SimpleRouter()
router.register("user",views.UserView,"user")
urlpatterns = [
path("admin/", admin.site.urls),
path("api/v1/", include(router.urls))
]
views.py
import uuid
from django.shortcuts import render
from .models import User, UserToken
from rest_framework.viewsets import ModelViewSet,ViewSet
from rest_framework.decorators import action
from rest_framework.response import Response
# Create your views here.
class UserView(ViewSet):
@action(methods=["POST"], detail=False)
def login(self, request):
username = request.data.get("username")
password = request.data.get("password")
user = User.objects.filter(username=username, password=password).first()
if user:
token = str(uuid.uuid4())
#用户登陆成功随机生成一个用不重复的字符串
#在Usertoken表中存储一下 :1 从来没有登陆过 插入一条 2登陆过 修改记录
UserToken.objects.update_or_create(user=user, defaults={"token": token})
#如果有就修改 没有就新增
#kwargs 传入的东西查找 能找到 使用defaults的更新 否则新增一条
return Response({"code": 100, "msg": "登陆成功", "token": token})
else:
return Response({"code": 101, "msg": "用户名或密码错误"})
"""
以后 有的接口组要登录后才能访问 有的接口不登陆就能访问(查询所有不需要登录就能访问 查询单个 需要登陆才能访问)
登录认证的限制
写登录接口 返回token 以后只要带着token过来 就是登陆了 不带就是没有登陆
"""
认证组件使用
# 查询所有不需要登录就能访问
# 查询单个,需要登录才能访问
# 在查询单个个视图类中写authentication_classes=[]
1.写一个认证类 需要继承BaseAuthentication
from rest_framework.authentication import BaseAuthentication
2.通过看源码分析 必须重写authenticate方法 在该方法中实现登录认证
3.如果认证成功 返回两个值【登录用户和token或None】
4.认证不通过 抛异常
from rest_framework.exceptions import AuthenticationFailed
5.局部使用和全局使用
局部:只在某视图类中使用【当前视图类管理的所有接口均使用过】
class BookView(ViewSetMixin,RetrieveAPIView):
queryset = Books.objects.all()
serializer_class = BooksSerializers
authentication_classes = [LoginAuth]
"""登录以后才能查询单个书信息"""
全局:全部接口都生效(登录接口不需要)
REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES": ["app01.authentication.LoginAuth"]
}
"""
所有接口都需要认证 登陆不能验证 局部禁用
"""
局部禁用:
class UserView(ViewSet):
authentication_classes = []
view.py
#查所有
class BooksView(ViewSetMixin,ListAPIView):
queryset = Books.objects.all()
serializer_class = BooksSerializers
#查单个
class BookView(ViewSetMixin,RetrieveAPIView):
queryset = Books.objects.all()
serializer_class = BooksSerializers
authentication_classes = [LoginAuth] #需要写一个认证类
authentication.py 认证类代码
#自己写的认证类 需要继承某个类
from .models import UserToken
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class LoginAuth(BaseAuthentication):
def authenticate(self,request):
#在这里实现用户的验证 如果是登录的继续走 不是就抛异常
#请求中是否携带token判断是否登录 地址栏中 request是新的
token = request.query_params.get("token",None)
if token: #前端传入token 去表中查 如果查到了登录了返回两个值 固定的 1.当前登录用户 2.token
user_token=UserToken.objects.filter(token=token).first()
if user_token:
return user_token.user,token
else:
raise AuthenticationFailed("token认证失败")#没登陆就抛异常
else:
raise AuthenticationFailed("token没传")
权限组件
即便登陆成功 有些接口 还是不能访问 因为没有权限
登陆后 有的接口有访问权限 有的没有
查询单个和查询所有 都要登录才能访问 -- 全局认证
查询单个需要超级管理员
查询所有需要登陆的用户
权限是一个字段 在User表中加入user_type
字段
权限的使用
1.写一个权限类 继承BasePermission
from rest_framework.permissions import BasePermission
2.重写has_permission方法 在该方法实现权限认证 在这方法中request.user就是当前登录用户
3.如果有权限 返回True
4.如果没有权限 返回False 定制返回中文 self.message = “中文”
5.局部使用和全局使用
局部:只在某个视图类中使用【当前视图类管理的所有接口都使用
】
class BookView(ViewSetMixin,RetrieveAPIView):
queryset = Books.objects.all()
serializer_class = BooksSerializers
authentication_classes = [LoginAuth] #需要写一个认证类
permission_classes = [CommonPermission] #认证和权限都可以加多个 多层
全局:全局所有接口都生效
"DEFAULT_PERMISSION_CLASSES": ["app01.permissions.CommonPermission"],
局部禁用:
permission_classes = []
views.py
class BookView(ViewSetMixin,RetrieveAPIView):
queryset = Books.objects.all()
serializer_class = BooksSerializers
authentication_classes = [LoginAuth] #需要写一个认证类
permission_classes = [CommonPermission] #认证和权限都可以加多个 多层
permission.py
#权限类 写一个类 重写方法 在方法中实现权限认证
from rest_framework.permissions import BasePermission
class CommonPermission(BasePermission):
#重写has_permission方法 有权限返回True,没有权限返回False
def has_permission(self,request,view):
#实现权限的控制 -- 要先知道当前的登录用户是谁 request.user 前面认证返回的登录用户 就是这里的request.user
if request.user.user_type == 1:
return True
else:
#没有权限 可以放一个message
self.message = "您是%s 您没有权限" %request.user.get_user_type_display()
return False
频率组件
控制某个接口访问频率(次数)
查询所有接口 同一个ip一分钟只能访问5次
使用步骤
1.写一个频率类 继承SimpleRateThrottle
from rest_framework.throttling import BaseThrottle,SimpleRateThrottle
2.重写get_cache_key方法 返回什么 就以什么做限制 先用ip
#客户端的ip地址 从哪里拿 HttpRequest.META里取
return request.META.get("REMOTE_ADDR")
3.配置一个类属性
scope = "lzy"
4.在配置文件中配置
"DEFAULT_THROTTLE_RATES": {
"lzy": "5/m",
},
5.局部使用和全局使用
局部:只在某个视图类中使用【当前视图类管理的所有接口都使用
】
throttle_classes = [CommonThrottle]
全局:全局所有接口都生效
"DEFAULT_THROTTLE_CLASSES": ["app01.throttling.CommonThrottle"],
局部禁用:
throttle_classes = []
过滤排序
必须继承GenericAPIView及其子类才能使用这种方式
只有查所有才用到过滤
内置过滤类的使用 继承GenericAPIView
#查所有
class BooksView(ViewSetMixin,ListAPIView):
queryset = Books.objects.all()
serializer_class = BooksSerializers
#这样就有过滤功能
filter_backends = [SearchFilter]
search_fields = ["name"] #按照名字模糊匹配 内置的 固定用法
search_fields = ["name","price"] #按照名字或价格模糊匹配 其中有一个是相同的都会被筛选出来
ulr = 127.0.0.1:8000/api/v1/books/?seach=书
使用第三方django-filter实现过滤
# 安装:django-filter
#查所有
class BooksView(ViewSetMixin,ListAPIView):
queryset = Books.objects.all()
serializer_class = BooksSerializers
#这样就有过滤功能
filter_backends = [DjangoFilterBackend]
filterset_fields = ["name"] #支持完整匹配 name=书
url = 127.0.0.1:8000/api/v1/books/?name=书2
url = 127.0.0.1:8000/api/v1/books/?name=书2&price=12
自己定制过滤类实现过滤
查询价格大于100的书
url = http://127.0.0.1:8000/api/v1/books/?price_gt=100
1.定义一个过滤类 继承BaseFilterBackend
from django_filters.rest_framework import DjangoFilterBackend
2.重写filter_queryset方法
def filter_queryset(self,request, queryset, view):
price_gt = request.query_params.get("price_gt",None)
if price_gt:
qs=queryset.filter(price__gt=int(price_gt))
return qs
else:
return queryset
3.配置在视图类
filter_backends = [CommentFilter]
"""可以定制多个 从左往右 依次执行"""
排序的使用
内置
from rest_framework.filters import OrderingFilter
filter_backends = [OrderingFilter,CommentFilter]
ordering_fields = ["price"]
#按照价格排序
支持查询的方法
http://127.0.0.1:8000/api/v1/books/?ordering=price
http://127.0.0.1:8000/api/v1/books/?ordering=-price
http://127.0.0.1:8000/api/v1/books/?ordering=-id,pric
分页
只有查询才有分页
drf内置了三个分页器 之前的东西一样用
第一种:PageNumberPagination
from rest_framework.pagination import LimitOffsetPagination,CursorPagination,PageNumberPagination
class CommonPageNumberPagination(PageNumberPagination):
page_size = 1 #每页显示两条
page_query_param = "page" #page=10 查询第10也的数据
page_size_query_param = "size" # page=10&size=5 查询第十页 每页显示5条
max_page_size = 10 #每页最大的显示条数
#配置在试图页即可
pagination_class = CommonCursorPagination
第二种:LimitOffsetPagination
from rest_framework.pagination import LimitOffsetPagination,CursorPagination,PageNumberPagination
class CommonPageNumberPagination(LimitOffsetPagination):
default_limit = 3 #每页显示3条
limit_query_param = "limit" #limit=3
offset_query_param = "offset" #offset=1 从第一个位置开始 取limit条
max_limit = 5
#配置在试图页即可
pagination_class = CommonCursorPagination
app用
class CommonPageNumberPagination(CursorPagination):
cursor_query_param = "cursor" #查询参数
page_size = 2 #每页多少条
ordering = "id" #排序字段
#配置在试图页即可
pagination_class = CommonCursorPagination
#游标分页 只能下一页 上一页 不能跳到中间 但他的效率最高 大数据量分页 使用这种较好