首页 > 其他分享 >drf 认证组件 权限组件 频率组件 过滤排序 分页

drf 认证组件 权限组件 频率组件 过滤排序 分页

时间:2023-02-08 21:57:16浏览次数:40  
标签:登录 ViewSetMixin 视图 user 组件 import 排序 class drf

昨日内容回顾

# 1 两个视图基类
    APIView
    GenericAPIView:跟数据库打交道,而且需要序列化反序列化,可以使用它
     
# 2 5个视图扩展类+GenericAPIView = 视图类
    -list
    -retrieve
    -destroy
    -create
    -update
    
    -正常需要写两个视图类,实现5个接口
        -BookView:list,create
        -BookDetailView:retrieve,destory,update
        -5个接口只想写新增和修改--->两个视图类
        -如果配合主动生成路由:ViewSetMixin+list+update+GenericAPIView  可以写到一个视图类中
        -5个接口:自动生成路由+5个视图扩展类+GenericAPIView--->一个视图类即可
            
# 3 9个视图子类 视图类
   -5个,两两组合,三个组合
    
# 4 视图集
   -ModelViewSet
   -ReadOnlyModelViewSet
   -ViewSetMixin:不是视图类,配合视图类一起用,重写了as_view--->路由写法变了--->本质就是通过传的actions做映射{'get':'xxx'}
        -传actions方式
        -自动生成方法
     
   -继承APIView+自动生成路由--->ViewSet
   -继承GenericAPIView+自动生成路由--->GenericViewSet
    
   -引申出来:后期在视图类中的方法,可以任意命名,只要做好映射,或自动生成即可
    
# 路由组件
   -三种写法
       -传统写法:不继承ViewSetMixin及其子类的
       -映射写法:传actions
       -自动生成写法:两个类,加入到路由中,两种(include,列表直接加)
       
   -action装饰器
       -自动生成路由时,做自动生成,装在视图类的方法上
    
# 7 登陆接口--->自动生成路由
   -ViewSetMixin+APIView   login--->使用action装饰器装饰--->post请求
   -取出前端传入的数据(三种格式都可以)--->request.data
   -写逻辑判断是否成功--->UserToken存储状态
      -如果存在修改,不存在就更新
   -返回给前端

1 认证组件

# 以后,有的接口需要登录后才能访问,有的接口,不登录就能访问
	-登录认证的限制

# 写一个登录接口,返回token,以后只要带着token过来,就是登录了,不带,就没有登录
	-User表
    -UserToken表:存储用户登录状态 [这个表可以没有,如果没有,把字段直接写在User表上也可以]
    
# 查询所有不需要登录就能访问
# 查询单个,需要登录才能访问

1.1 登录接口

# 表模型

class User(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)

class UserToken(models.Model):  # 跟User是一对一
    token = models.CharField(max_length=32)
    user = models.OneToOneField(to='User', on_delete=models.CASCADE, null=True)
    # user :反向,表名小写,所有有user字段

image-20230207194053842

# 路由
from django.contrib import admin
from django.urls import path, include
from app01 import views
from rest_framework.routers import SimpleRouter

router = SimpleRouter()

router.register('user', views.UserView, 'user')  # /api/v1/login  post 请求

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include(router.urls)),
]

image-20230207194124508

# 视图类
# 登录接口  自动生成路由+由于登录功能,不用序列化,继承ViewSet
from .models import User, UserToken
import uuid


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:
            # 用户存在,登录成功
            # 生成一个随机字符串--uuid
            token = str(uuid.uuid4())  # 生成一个永不重复的随机字符串
            # 在userToken表中存储一下:1 从来没有登录过,插入一条,     2 登录过,修改记录
            # 如果有就修改,如果没有就新增  (if 自己写)
            # kwargs 传入的东西查找,能找到,使用defaults的更新,否则新增一条
            UserToken.objects.update_or_create(user=user, defaults={'token': token})
            return Response({'code': '100', 'msg': '登录成功', 'token': token})
        else:
            return Response({'code': '101', 'msg': '用户名或密码错误'})

image-20230207194150956

from rest_framework.generics import ListAPIView, RetrieveAPIView
from rest_framework.viewsets import ViewSetMixin


# 查询所有 查询单个
# class BookView(ViewSetMixin, ListAPIView,RetrieveModelMixin):
# 查询所有
class BookView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
# 查询单个
class BookDetailView(ViewSetMixin, RetrieveAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

image-20230207210530338

# 路由
from django.urls import path, include
from app01 import views
from rest_framework.routers import SimpleRouter

router = SimpleRouter()

# router.register('user', views.UserView, 'user')  # /api/v1/login  post 请求
router.register('books', views.BookView, 'books')
router.register('books', views.BookDetailView, 'books')

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include(router.urls)),
]

image-20230207210633126

1.2 认证组件使用步骤(代码)

# 查询所有不需要登录就能访问
# 查询单个,需要登录才能访问

视图类

from rest_framework.generics import ListAPIView, RetrieveAPIView
from rest_framework.viewsets import ViewSetMixin
from .authenticate import LoginAuth

# 查询所有 查询单个
# class BookView(ViewSetMixin, ListAPIView,RetrieveModelMixin):
# 查询所有
class BookView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    
# 查询单个
class BookDetailView(ViewSetMixin, RetrieveAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    authentication_classes = [LoginAuth]  # 需要写一个认证类,需要咱们写

image-20230207212845132

认证类

# 自己写的认证类,继承某个类
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from .models import UserToken


class LoginAuth(BaseAuthentication):
    def authenticate(self, request):
        # 在这里实现认证,如果是登录的,继续往后走,如果不是抛异常
        # 请求中是否携带token,判断是否登录 放在地址栏中
        token = request.query_params.get('token', None)
        if token:  # 前端传入token了,去表中查,如果能查到,登录了,返回两个值[固定的,当前登录用户,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没有传')

image-20230207212919313

路由

from django.contrib import admin
from django.urls import path, include
from app01 import views
from rest_framework.routers import SimpleRouter

router = SimpleRouter()

router.register('user', views.UserView, 'user')  # /api/v1/login  post 请求
router.register('books', views.BookView, 'books')
router.register('books', views.BookDetailView, 'books')

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include(router.urls)),
]

image-20230207213149985

image-20230207213328527

image-20230207213359828

1.3 认证组件使用步骤

# 1 写一个认证类,继承BaseAuthentication
   from rest_framework.authentication import BaseAuthentication
    
# 2 重写authenticate方法,在该方法在中实现登录认证:token在哪带的?如何认证它是登录了
     
# 3 如果认证成功,返回两个值【返回None或两个值】
    前端传入token了,去表中查,如果能查到,登录了,返回两个值[固定的:当前登录用户,token]

# 4 认证不通过,抛异常AuthenticationFailed
    from rest_framework.exceptions import AuthenticationFailed
    
5 局部使用和全局使用

局部

-局部:只在某个视图类中使用【当前视图类管理的所有接口】
class BookDetailView(ViewSetMixin, RetrieveAPIView):
            authentication_classes = [LoginAuth]

全局

 -全局:全局所有接口都生效(登录接口不要)
    REST_FRAMEWORK = {
    	'DEFAULT_AUTHENTICATION_CLASSES':['app01.authenticate.LoginAuth']
	}

image-20230207221156184

# 坑
	-不要在配置文件中乱导入不使用的东西,否则会报错

登录接口禁用

登录接口不要认证

image-20230207221439714

局部禁用

 -局部禁用:
    	 class BookDetailView(ViewSetMixin, RetrieveAPIView):
            authentication_classes = [] 

2 权限组件

# 即便登录成功了,有些接口,还是不能访问,因为没有权限

# 登录后,有的接口有权限访问,有的没有权限访问

# 查询单个和查询所有,都要登录才能访问----》全局认证
	-查询单个需要超级管理员才能访问
    -查询所有,所有登录用户都能访问
    
    
# 权限是一个字段,在User表中,加入user_type字段

2.1 权限的使用(代码)

模型类

user_type = models.IntegerField(choices=((1, '超级管理员'), (2, '普通用户'), (3, '垃圾用户')),default=2)

image-20230207223502181

权限类

# 权限类,写一个类,继承基类,重写某个方法,在方法中实现权限认证,如果有权限return True,如果没有权限,返回False
from rest_framework.permissions import BasePermission


class CommonPermission(BasePermission):
    def has_permission(self, request, view):
        # 实现权限的控制--->得知道当前登录用户是谁? 当前登录用户是 request.user
        if request.user.user_type == 1:
            return True
        else:
            return False

image-20230207224730119

视图类

from .permissions import CommonPermission
class BookDetailView(ViewSetMixin, RetrieveAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    # authentication_classes = [LoginAuth]  # 需要写一个认证类,需要咱们写
    permission_classes = [CommonPermission] # 需要写一个权限类,需要咱们写

image-20230207224909552

image-20230207225406192

image-20230207225452436

image-20230207225535937

image-20230207225604026

2.2 自定义返回权限提示

 # 没有权限,可以向对象中放一个属性 message
            # 如果表模型中,使用了choice,就可以通过 get_字段名_display() 拿到choice对应的中文
            self.message='您是[%s],您没有权限' %request.user.get_user_type_display()

image-20230207230135503

image-20230207230224831

2.3 权限的使用

# 1 写一个权限类,继承BasePermission
   from rest_framework.permissions import BasePermission

# 2 重写has_permission方法,在该方法在中实现权限认证,在这方法中,request.user就是当前登录用户
# 3 如果有权限,返回True
# 4 没有权限,返回False,定制返回的中文: self.message='中文'
# 5 局部使用和全局使用

局部

-局部:只在某个视图类中使用【当前视图类管理的所有接口】
       class BookDetailView(ViewSetMixin, RetrieveAPIView):
    		permission_classes = [CommonPermission]

全局

 'DEFAULT_PERMISSION_CLASSES': ['app01.permissions.CommonPermission'],

image-20230207230742889

局部禁用

 -局部禁用:
    	 class BookDetailView(ViewSetMixin, RetrieveAPIView):
            permission_classes = [] 

image-20230207230925504

3 频率组件

# 控制某个接口访问频率(次数)


# 查询所有接口,同一个ip一分钟只能访问5次

3.1 频率的使用(代码)

频率类

# 频率类,不继承BaseThrottle,继承SimpleRateThrottle,少写代码
from rest_framework.throttling import BaseThrottle, SimpleRateThrottle

class CommonThrottle(SimpleRateThrottle):
    # 类属性,属性值随便写
    # 配置文件中配置
    scope = 'jason'

    def get_cache_key(self, request, view):
        # 返回什么,就以什么做频率限制[可以返回ip 或用户ID]
        # 客户端的ip地址从哪拿?
        return request.META.get('REMOTE_ADDR')  # 以ip做限制
        # return request.user.pk  # 以用户id做限制

image-20230207232121610

视图类

# 查询单个
from .permissions import CommonPermission
from .throttling import CommonThrottle


class BookDetailView(ViewSetMixin, RetrieveAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    # authentication_classes = [LoginAuth]  # 需要写一个认证类,需要咱们写
    permission_classes = [CommonPermission]  # 需要写一个权限类,需要咱们写
    throttle_classes = [CommonThrottle]  # 需要写一个频率类,需要咱们写

image-20230207232308661

image-20230207232410461

3.2 频率的使用

# 1 写一个频率类,继承SimpleRateThrottle
    from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
# 2 重写get_cache_key方法,返回什么,就以什么做限制----》ip,用户id做限制
# 3 配置一个类属性:scope = 'book_5_m'
# 4 在配置文件中配置
  'DEFAULT_THROTTLE_RATES': {
        'book_5_m': '5/m',
    },

image-20230207232831659

# 5 局部使用和全局使用

局部

-局部:只在某个视图类中使用【当前视图类管理的所有接口】
        class BookDetailView(ViewSetMixin, RetrieveAPIView):
    		throttle_classes = [CommonThrottle]

全局

 -全局:全局所有接口都生效
          REST_FRAMEWORK = {
             'DEFAULT_THROTTLE_CLASSES': ['app01.throttling.CommonThrottle'],
        }

image-20230207233411615

局部禁用

-局部禁用:
    	 class BookDetailView(ViewSetMixin, RetrieveAPIView):
            throttle_classes = [] 

image-20230207233550673

4 过滤

# restful规范中,要求了,请求地址中带过滤条件
	-5个接口中,只有一个接口需要有过滤和排序,查询所有接口
    
# 查询 所有图书接口,查询以  红  开头的所有图书

方式一:内置过滤类的使用【继承GenericAPIView】

# 1 内置过滤类
from rest_framework.filters import SearchFilter

class BookView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    #  SearchFilter内置的,固定用法,模糊匹配
    #  就有过滤功能了,指定按哪个字段过滤
    filter_backends = [SearchFilter]
    # search_fields = ['name']  # 可以按名字模糊匹配
    search_fields = ['name','price']  # 可以按名字模糊匹配或价格模糊匹配
    
# 可以使用的搜索方式
	http://127.0.0.1:8000/api/v1/books/?search=红  # name或price中只要有红就会搜出来
                    
# 继承APIView如何写,完全自己写,麻烦,但是清晰

image-20230208164755134

image-20230208164816264

方式二:使用第三方djagno-filter实现过滤

下载 pip3 install django-filter

# 2 第三方过滤类
from django_filters.rest_framework import DjangoFilterBackend


class BookView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    permission_classes = []
    authentication_classes = []
    throttle_classes = []
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['name', 'price']  # 支持完整匹配 name=红楼梦
    
    
 # 支持的查询方式
http://127.0.0.1:8000/api/v1/books/?price=299
http://127.0.0.1:8000/api/v1/books/?price=333&name=红楼梦

image-20230208172325427

image-20230208172341380

方式三:自己定制过滤类实现过滤

# 查询价格大于100的所有图书
	http://127.0.0.1:8000/api/v1/books/?price_gt=100     

过滤类

from rest_framework.filters import BaseFilterBackend
#第一步: 定义一个过滤类,继承BaseFilterBackend,重写filter_queryset方法
class CommonFilter(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        # 在里面实现过滤,返回qs对象,就是过滤后的数据
        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

image-20230207235535448

视图类

from .MyFliter import CommonFilter

class BookView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    filter_backends = [CommonFilter]  # 可以定制多个,从左往右,依次执行

image-20230208191011192

image-20230208190935990

5 排序

# 内置就够了
from rest_framework.filters import OrderingFilter

class BoosView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [OrderingFilter, ]
    # 按价格排序
    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,price
        先按反序id排如果有相同的按价格正序排

6 分页

分页,只有查询所有接口,才有分页

drf内置了三个分页器,对应三种分页方式

内置的分页类不能直接使用,需要继承,定制参数后才能使用

分页使用,自定义一个分页类(三种)

from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination

方式一:PageNumberPagination(网页推荐)

# 分页器使用, 自定义一个分页类(三种)
class CommonPageNumberPagination(PageNumberPagination):
    # 每页显示 2条
    page_size = 2
    # page=10 查询第10页
    page_query_param = 'page'
    # page=10&size=5 第10页显示5条
    page_size_query_param = 'size'
    # 每页最大显示 5 条
    max_page_size = 5

image-20230208204321712

image-20230208204258197

image-20230208204449598

方式二:LimitOffsetPagination

# 第二种
class CommonLimitOffsetPagination(LimitOffsetPagination):
    default_limit = 2  # 每页显示2条
    limit_query_param = 'limit'  # limit=3 取3条
    offset_query_param = 'offset'  # offset=1 从第一个位置开始,取limit条
    max_limit = 5
    # offset=3&limit=2     0 1 2 3 4  取3 4

image-20230208210056162

from .page import CommonLimitOffsetPagination as LimitOffsetPagination

class BoosView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    # 认证
    authentication_classes = []
    # 权限
    permission_classes = []
    # 频率
    throttle_classes = []
    # 偏移分页
    pagination_class = LimitOffsetPagination  

image-20230208210117610

image-20230208210616187

方式三:CursorPagination(app推荐)

这种使用于app,同时也是效率最高的

# 第三种 app推荐
class CommonCursorPagination(CursorPagination):
    cursor_query_param = 'cursor'  # 查询参数
    page_size = 2  # 每页多少条
    ordering = 'id'  # 排序字段

image-20230208213724366

from .page import CommonCursorPagination as CursorPagination

class BoosView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    # 认证
    authentication_classes = []
    # 权限
    permission_classes = []
    # 频率
    throttle_classes = []
    # 游标分页,只能下一页,上一页,不能跳到中间,但它效率高,大数据量分页,使用这种较好
    pagination_class = CursorPagination

标签:登录,ViewSetMixin,视图,user,组件,import,排序,class,drf
From: https://www.cnblogs.com/super-xz/p/17103422.html

相关文章