视图集回顾
1 认证组件
1.1 登录功能
1.2 认证组件
2 权限组件
2.1 django项目国际化
3 频率组件
4 排序
视图集回顾
# 1 9 个视图子类
-GenericAPIView+5个视图扩展类的组合
-ListAPIView,CreateAPIView,DestroyAPIView,RetrieveAPIView,UpdateAPIView
-ListCreateAPIView
-RetrieveDestroyAPIView,RetrieveUpdateAPIView,RetrieveUpdateDestroyAPIView
-class TestView(ListAPIView):
queryset
serializer_calss
# 2 视图集
-ModelViewSet:GenericViewSet+5个视图扩展类
-路由写法变了的5个接口
-PublishView.as_view({'get':'list','post':'create'})
-自动生成路由:本质:把get和list对应,把post和create对应
-可以重写父类方法,完成自己的定制
-ReadOnlyModelViewSet:2个接口,只读的
-ViewSet:ViewSetMixin+APIView
-GenericViewSet:ViewSetMixin+GenericAPIView
-ViewSetMixin:重写了as_view 路由写法变了
# 3 drf路由,继承ViewSetMixin+APIView
ViewSetMixin+ListAPIView---》自动生成路由
-自动生成路由步骤
-action装饰器---》/publish/被装饰函数名字/ post请求就会执行
- @action(methods=['POST'], detail=False)
# 4 视图类对象中:self.action 路由匹配成功,执行视图类中方法的名字的字符串
# 补充:
# 如果自动生成路由,哪个请求能执行这个list?路由匹配成功后的get请求,就会执行这个list,自动生成的
class TestView(GenericViewSet):
def list(self, request):
return Response('sdafs')
1 认证组件
1.1 登录功能
# 写登录的时候,可以使用auth提供的user表,也可以自定义---》自定义
# 写登录接口,登录成功,要有标志,生成一个随机字符串,放到表中,以后它只要带着这个随机字符串过来,我们就认为是这个人登录的
### ### ### ### ### ### ### 视图类### ### ### ### ### ### ### ### ### ### ### ### ###
from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
from rest_framework.viewsets import ViewSetMixin, ViewSet
from rest_framework.response import Response
from rest_framework.decorators import action
from .models import User, UserToken
import uuid
# class UserView(ViewSetMixin,APIView):
class UserView(ViewSet):
@action(methods=['POST'], detail=False) # /user/login/ post 请求就会执行
def login(self, request, *args, **kwargs):
# 前端传入用户名密码
username = request.data.get('username')
password = request.data.get('password')
user = User.objects.filter(username=username, password=password).first()
if user:
# 生成一个随机字符串,返回给前端,并且要把随机字符串存到token表中
# 随机字符串使用uuid生成
token = str(uuid.uuid4())
# 把随机字符串存到token表中会有两种情况(如果之前没有登录过就是新增,如果之前登录过修改)
# 先去UserToken表中,根据user查,如果能查到,就修改,查不到就新增一条记录
##### 方式一:麻烦方式
# user_token=UserToken.objects.filter(user=user).first()
# if user_token:
# user_token.token=token
# user_token.save()
# else:
# UserToken.objects.create(user=user,token=token)
## 方式二:通过user去UserToken表中查,如果能查到用defaults的更新,如果查不到,就用user和defaults新增一条记录
UserToken.objects.update_or_create(defaults={'token': token}, user=user)
return Response({'code': 100, 'msg': '登录成功', 'token': token, 'username': user.username})
else:
return Response({'code': 101, 'msg': '用户名或密码错误'})
### ### ### ### ### ### ### 路由### ### ### ###
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('user', UserView, 'user')
urlpatterns = [
path('admin/', admin.site.urls),
path('', include(router.urls)),
]
1.2 认证组件
# 认证有什么作用?
-一些接口,想限制登录后才能访问,没登录不能访问
-做登录认证,限制如果没登录,不允许访问该接口
# drf中,认证类如何使用
# 1 写一个类,继承BaseAuthentication
# 2 类中重写 :authenticate方法
# 3 在authenticate完成登录认证,如果登录了,返回两个值,如果没登录抛异常
# 4 在视图类中配置使用
class BookView(ViewSet):
authentication_classes = [LoginAuth]
# 如果登录了,在视图类的方法中,能拿出当前登录用户
request.user
1.2.1 auth.py
from .models import UserToken
# 按照如下步骤写
# 1 写一个类,继承BaseAuthentication
# 2 类中重写 :authenticate方法
# 3 在authenticate完成登录认证,如果登录了,返回两个值,如果没登录抛异常
# 4 在视图类中配置使用
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class LoginAuth(BaseAuthentication):
def authenticate(self, request):
# 如何知道当前请求这个人是登录了?
# 拿到前端传入的token:当时给的随机字符串---》去UserToken表中查,如果能查到,说明就是这个人在访问,如果查不到,说明这个人没有登录过
# 前端传入的token,从哪拿?如何拿?---》后端定的:1 请求地址中 2 请求体中 3 请求头中
token = request.query_params.get('token')
user_token = UserToken.objects.filter(token=token).first()
if user_token: # 是登录状态
return user_token.user, token # 返回两个值,第一个是当前登录用户,第二个是前端token
else:
raise AuthenticationFailed('您没有登录')
1.2.2 视图类
from django.shortcuts import render
# Create your views here.
# 路由自动生成吗? 自动生成继承:ViewSetMixin 我想
# 要不要序列化,要不要跟数据库打交道,继承GenericAPIView:查出所有数据(只要一条),还要写个序列化类()
from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
from rest_framework.viewsets import ViewSetMixin, ViewSet
from rest_framework.response import Response
from rest_framework.decorators import action
from .models import User, UserToken
import uuid
from .auth import LoginAuth
# class UserView(ViewSetMixin,APIView):
class UserView(ViewSet):
@action(methods=['POST'], detail=False) # /user/login/ post 请求就会执行
def login(self, request, *args, **kwargs):
# 前端传入用户名密码
username = request.data.get('username')
password = request.data.get('password')
user = User.objects.filter(username=username, password=password).first()
if user:
# 生成一个随机字符串,返回给前端,并且要把随机字符串存到token表中
# 随机字符串使用uuid生成
token = str(uuid.uuid4())
# 把随机字符串存到token表中会有两种情况(如果之前没有登录过就是新增,如果之前登录过修改)
# 先去UserToken表中,根据user查,如果能查到,就修改,查不到就新增一条记录
##### 方式一:麻烦方式
# user_token=UserToken.objects.filter(user=user).first()
# if user_token:
# user_token.token=token
# user_token.save()
# else:
# UserToken.objects.create(user=user,token=token)
## 方式二:通过user去UserToken表中查,如果能查到用defaults的更新,如果查不到,就用user和defaults新增一条记录
UserToken.objects.update_or_create(defaults={'token': token}, user=user)
return Response({'code': 100, 'msg': '登录成功', 'token': token, 'username': user.username})
else:
return Response({'code': 101, 'msg': '用户名或密码错误'})
class BookView(ViewSet):
authentication_classes = [LoginAuth] # 这个认证类,管理了当前类下所有的方法
def list(self, request):
print(request.user.username) # 当前登录用户
return Response("你好:%s,你看到了好多书啊"%request.user.username)
1.2.3 路由
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('user', UserView, 'user')
router.register('books', BookView, 'books')
urlpatterns = [
path('', include(router.urls)),
]
1.3 小结
# 1 认证类写好,使用
-配置在视图类上---》局部使用
-配置文件中配置--》全局使用---》所有接口都必须登录后才能用
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'app01.auth.LoginAuth'
],
}
-局部禁用:
class UserView(ViewSet):
authentication_classes = []
# 2 写的认证类:要重写authenticate方法,必须返回两个参数:当前登录用户:user_token.user,用户的 token----》后续再视图类中:request.user 就是认证类返回的第一个参数,request.auth 就是认证类返回的第二个参数
# 3 如果认证失败,抛异常AuthenticationFailed,会被drf捕获,处理,不会报错到前端
# 4 前端传入的token,从哪取?
-后端定的,我们这个项目是从请求地址中取
-还可以从请求头或请求体中取
# 5 UserToken.objects.update_or_create
2 权限组件
# 系统中:有普通用户,超级用户,超级管理员,他们都登录了,又分权限,有的人有权限,就能访问这个接口,没权限,就不能访问
# 使用步骤:
# 1 写一个类,继承 BasePermission
# 2 重写 has_permission
# 3 在方法中校验用户是否有权限,如果有,就返回True,如果没有,就返回False
-由于它的执行是在认证之后,所有从request.user中取出当前等用户,判断权限
# 4 在视图类中局部使用,在settings中全局使用,局部可以禁用
class PublishView(ViewSet):
permission_classes = [UserPermission]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'app01.auth.LoginAuth'
],
'DEFAULT_PERMISSION_CLASSES': [
'app01.permissions.UserPermission',
],
}
from rest_framework.permissions import BasePermission
# 1 写一个类,继承 BasePermission
# 2 重写 has_permission
# 3 在方法中校验用户是否有权限,如果有,就返回True,如果没有,就返回False
# 4
class UserPermission(BasePermission):
def has_permission(self, request, view):
# request 当次请求的request, 新的,它是在认证类之后执行的,如果认证通过了request.user 就是当前登录用户
# 拿到当前登录用户,查看它的类型,确定有没有权限
if request.user.user_type == 3:
return True
else:
self.message = '您的用户类型是:%s,您没有权限操作' % (request.user.get_user_type_display())
return False
2.1 django项目国际化
# 配置文件中--->以后所有英文都会转成中文
INSTALLED_APPS = [
'rest_framework'
]
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
3 频率组件
# 控制一个接口,访问频次,比如一分钟只能访问1次
# 对接口进行访问次数限制
# 使用步骤:
# 1 写一个类,继承SimpleRateThrottle
# 2 重写get_cache_key,返回什么,就以什么做限制: IP地址,用户id限制
# 3 写一个类属性 scope = 'drf_day08'
# 4 配置文件中配置
'''
'DEFAULT_THROTTLE_RATES': {
'drf_day08': '3/m', # 一分钟访问三次 5/s,m,h,d
},
'''
# 5 局部使用,全局使用,局部禁用
class PublishView(ViewSet):
throttle_classes = [IPRateThrottle]
#6 全局使用
'DEFAULT_THROTTLE_CLASSES': ['app01.throttling.IPRateThrottle'],
from rest_framework.throttling import SimpleRateThrottle
# 1 写一个类,继承SimpleRateThrottle
# 2 重写get_cache_key,返回什么,就以什么做限制: IP地址,用户id限制
# 3 写一个类属性 scope = 'drf_day08'
# 4 配置文件中配置
'''
'DEFAULT_THROTTLE_RATES': {
'drf_day08': '3/m', # 一分钟访问三次
},
'''
# 5 局部使用,全局使用,局部禁用
class IPRateThrottle(SimpleRateThrottle):
scope = 'drf_day08' # 写一个类属性
def get_cache_key(self, request, view):
# 返回ip,以ip地址限制
print(request.META)
return request.META.get('REMOTE_ADDR')
4 排序
# 只有5个接口中的查询所有,才涉及到排序
# 查询所有用户---》接口
# 使用步骤
- 1 必须写在继承:GenericAPIView 类的视图类中才行
-2 配置类属性:
filter_backends = [OrderingFilter]
ordering_fields=['id','user_type'] #可以排序的字段
-3 使用:规定必须使用ordering 降序带负号
http://127.0.0.1:8000/user/?ordering=user_type #用户类型升序排
http://127.0.0.1:8000/user/?ordering=-user_type #用户类型降序排
http://127.0.0.1:8000/user/?ordering=user_type,-id#先按用户类型升序排,如果用户类型一样,再按id降序排
代码展示
# 由于要和数据库打交道,这里不适合用ViewSet, 而要用GenericViewSet
# 但是多了两个功能 queryset 和 serializer_class,但不影响我们的使用
from rest_framework.viewsets import GenericViewSet
from rest_framework.generics import ListAPIView # 继承ListAPIView可以
from rest_framework.mixins import ListModelMixin # 继承ListModelMixin也可以
# 排序
from rest_framework.filters import OrderingFilter
from .models import User
from .serializer import UserSerializer
# 演示排序,写查询所有用户,视图类必须继承GenericAPIView
# 视图类 + 扩展类,要和数据库打交道
class UserView(GenericViewSet, ListModelMixin):
# 配置了全局是,局部禁用认证
authentication_classes = []
# 局部禁用权限
permission_classes = []
# 局部禁用频率
throttle_classes = []
queryset = User.objects.all()
serializer_class = UserSerializer
# 排序
filter_backends = [OrderingFilter]
# 指定按照什么排序
ordering_fields = ['id', 'user_type']
@action(methods=['POST'], detail=False) # 访问 /user/login/ post请求就会执行
def login(self, request, *args, **kwargs):
# 前端传入用户名密码
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.objects.update_or_create(defaults={'token': token, 'user': user})
return Response({'code': 100, 'msg': '登录成功', 'token': token})
return Response({'code': 101, 'msg': '用户名或密码错误'})