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

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

昨日回顾

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

今日内容概要

  • 1 认证组件

    • 1.1 认证组件使用步骤
    • 1.2 代码
  • 2 权限组件

    • 2.1 权限的使用
  • 3 频率组件

    • 3.1 使用步骤
  • 4 过滤排序

    • 4.0 继承APIView 自己写
    • 4.1 内置过滤类的使用【继承GenericAPIView】
    • 4.2 使用第三方django-filter实现过滤
    • 4.3 自己定制过滤类实现过滤
    • 4.4 排序的使用
  • 5 分页

今日内容详细

0 事前准备

# 表创建,用户,用户状态码,外键,只读字段
'''熟练于心'''
# 序列化类
'''
from rest_framework import serializers
from .models import Book


class BookSerializer(serializers.ModelSerializer):
    # 跟表有关系
    class Meta:
        model = Book
        fields = ['id', 'name', 'price', 'publish_detail', 'author_list', 'publish', 'authors']
        extra_kwargs = {
            'name': {'max_length': 8},
            'publish_detail': {'read_only': True},
            'author_list': {'read_only': True},
            'publish': {'write_only': True},
            'authors': {'write_only': True},
        }
'''
# 查询多条
'''
class BookView(ViewSetMixin, ListAPIView):
    queryset =Book.objects.all()
    serializer_class = BookSerializer
'''
# 查询单条
'''
class BookDetailView(ViewSetMixin, RetrieveAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
'''
# 登录
'''
class UserView(ViewSet):
    # action匹配post,
    @action(methods=['POST'], detail=False)
    def login(self, requset):
        # request.data是拿前端传过来的数据
        username = requset.data.get('username')
        password = requset.data.get('password')
        user = User.objects.filter(username=username, password=password).first()
        if user:
            # 用户匹配成功,存在,生成随机字符串
            token = uuid.uuid4()
            UserToken.objects.update_or_create(user=user, defaults={'token': token})
            return Response({'code': 10000, 'msg': '登录成功', 'token': token})
        else:
            return Response({'code': 10001, 'msg': '账号或密码错误'})
'''
# 自动生成路由,需要配合ViewSetMixin
'''
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import SimpleRouter
from app01 import views

router = SimpleRouter()

router.register('books', views.BookView, 'books')
router.register('user', views.UserView, 'user')
router.register('book', views.BookDetailView, 'book')


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

1 认证组件

# 以后 有的接口需要登录之后才能访问 有的接口不登录就能访问
	登录认证的限制
    
# 写一个登录接口 返回token 以后只要带着token过来 就是登录了 不带 就没有登录


# 实现功能:
# 查询所有不需要登录就能访问
# 查询单个 需要登录才能访问

1.1 认证组件使用步骤

# 1 写一个认证类 继承BaseAuthentication
# 2 重写authenticate方法 在该方法中实现登录认证:token在哪带的?在网址中带 获取方式token = request.query_params.get('token', None) 如果认证它是登录了
# 3 如果认证成功 返回两个值【返回None或两个值】user_token.user, token
# 4 认证不通过 抛异常AuthenticationFailed
# 5 局部使用和全局使用:
'''
局部生效:只在某个视图类中使用【当前视图类管理的所有接口】
class BookDetailView(ViewSetMixin, RetrieveAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    authentication_classes = [LoginAuth]  # 为空则不生效
    
全局:全局所有接口都生效(登录接口不要)
在settings内加入
REST_FRAMEWORK = {
	'DEFAULT_AUTHENTICATION_CLASSES':					['app01.authenticate.LoginAuth']
全局生效之后把局部填为空即可局部不生效,因为生效顺序是:局部 全局 内置
局部禁用
class BookDetailView(ViewSetMixin, RetrieveAPIView):
	authentication_classes = [] 
'''

1.2 认证组件代码

# 认证类代码
'''
from rest_framework.authentication import BaseAuthentication
from app01.models import User, UserToken
from rest_framework.exceptions import AuthenticationFailed


class LoginAuth(BaseAuthentication):
    def authenticate(self, request):
        # 在这里实现认证 如果是登录的 继续往后走返回两个值 如果不是抛异常
        # 请求中是否携带token 判断是否登录 放在地址栏中
        token = request.query_params.get('token', None)
        # 前端网址中的token数据 格式为:?token:123
        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没传')
'''

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

2 权限组件

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

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

# 查询单个和查询所有 都要登录才能访问---> 全局认证

# 权限是一个字段 在User表中 加入user_type字段

# 实现功能:
查询单个需要超级管理员才能访问
查询所有 所有用户登录即可

2.1 权限的使用

# 1 写一个权限类 继承BasePermission
# 2 重新has_permission方法 在该方法中实现权限认证 在这个方法中 request.user就是当前用户(在源码中)
# 3 如果有权限 返回True
# 4 没有权限 返回False 定制返回的中文:self.message='中文'
# 4 局部使用和全局使用
	局部:只在某个视图类中使用【当前视图类管理的所有接口】
        class BookDetailView(ViewSetMixin, RetrieveAPIView):
    		permission_classes = [CommonPermission]
    全局:全局所有接口都生效
              REST_FRAMEWORK = {
            'DEFAULT_PERMISSION_CLASSES': [
                'app01.permissions.CommonPermission',
            ],

        }
     局部禁用:
    	 class BookDetailView(ViewSetMixin, RetrieveAPIView):
            permission_classes = [] 
permissions代码
'''
# 写权限类 写一个类 继承基类BasePermission,重写has_permission方法,在方法中实现权限认证 如果有权限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:
            # 没有权限 向对象中放一个属性 message
            # 如果表模型中使用了choice 就可以通过 get_字段名_display() 拿到choice对应的中文
            self.message = '您是【%s】, 您没有权限' % request.user.get_user_type_display()
            return False
'''

3 频率组件

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

# 实现需求:
查询所有接口 同一个ip一分钟只能访问5次

3.1 使用步骤

# 1 写一个频率类 继承SimpleRateThrottle
# 2 重写get_cache_key方法 返回什么 就以什么做限制---> ip 用户id都可以
# 3 配置一个类属性:scope = 'zpf_book_5_m'
# 4 在配置文件中配置
'DEFAULT_THROTTLE_RATES': {'zpf_book_5_m': '5/m'},
# 5 局部使用和全局使用
	-局部:只在某个视图类中使用【当前视图类管理的所有接口】
        class BookDetailView(ViewSetMixin, RetrieveAPIView):
    		throttle_classes = [CommonThrottle]
    -全局:全局所有接口都生效
          REST_FRAMEWORK = {
             'DEFAULT_THROTTLE_CLASSES': ['app01.throttling.CommonThrottle'],

        }
        -局部禁用:
    	 class BookDetailView(ViewSetMixin, RetrieveAPIView):
            throttle_classes = [] 
频率类代码
'''
# 频率类 不继承BaseThrottle,继承SimpleRateThrottle 少写代码
from rest_framework.throttling import BaseThrottle, SimpleRateThrottle


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

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

4 过滤排序

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

4.0 继承APIView自己写

class BookView(APIView):
    def get(self,request):
        search=request.query_params.get('search')
        books=Book.objects.filter()

4.1 内置过滤类的使用【继承GenericAPIView】

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中只要有红就会搜出来

4.2 使用第三方django-filter实现过滤

from django_filters.rest_framework import DjangoFilterBackend

# Create your views here.


class BookView(ViewSetMixin, ListAPIView):
    queryset =Book.objects.all()
    serializer_class = BookSerializer
    # 认证局部禁用
    authentication_classes = []
    # 权限局部禁用
    permission_classes = []
    # 频率局部jiny
    throttle_classes = []
    # 过滤方式1.内置过滤类的使用
    # filter_backends = [SearchFilter]
    # search_fields = ['name']  # 可以按名字模糊匹配
    # search_fields = ['name', 'price']  # 可以按名字或价格模糊匹配
    # 过滤方式2.第三方模块django_filter
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['name', 'price']  # 必须完整匹配, name=红楼梦&price=11
    
    
# 支持的查询方式
http://127.0.0.1:8000/api/v1/books/?price=939
http://127.0.0.1:8000/api/v1/books/?price=939&name=红楼猛

4.3 自己定制过滤类实现过滤

# 实现需求:查询价格大于100的所有图书
http://127.0.0.1:8000/api/v1/books/?price_gt=100
# 第一步:定义一个过滤类 继承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

# 第二步:配置在视图类上
from app01.three_auth.filter import CommonFilter

# Create your views here.


class BookView(ViewSetMixin, ListAPIView):
    queryset =Book.objects.all()
    serializer_class = BookSerializer
    # 认证局部禁用
    authentication_classes = []
    # 权限局部禁用
    permission_classes = []
    # 频率局部jiny
    throttle_classes = []
    # 过滤方式1.内置过滤类的使用
    # filter_backends = [SearchFilter]
    # search_fields = ['name']  # 可以按名字模糊匹配
    # search_fields = ['name', 'price']  # 可以按名字或价格模糊匹配
    # 过滤方式2.第三方模块django_filter
    # filter_backends = [DjangoFilterBackend]
    # filterset_fields = ['name', 'price']  # 必须完整匹配, name=红楼梦&price=11
    # 过滤方式3.自定义过滤类
    filter_backends = [CommonFilter]  # 可以定制多个 从左往右执行
    

4.4 排序的使用

# 内置的就够了
class BookView(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

5 分页

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

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

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


# 分页使用 自定义一个分页类(三种)
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination


# 网页用它
class CommonPageNumberPagination(PageNumberPagination):
    page_size = 2  # 每页显示2条
    page_query_param = 'page'  # page=10 查询低10页的数据 每页显示2条
    page_size_query_param = 'size'  # page=10&size=5 查询低10页 每页显示5条
    max_page_size = 5  # 每页最大显示10条


# 第二种,视情况而定,这种用着不太舒服
class CommonLimitOffsetPagination(LimitOffsetPagination):
    default_limit = 3  # 每页显示3条
    limit_query_param = 'limit'  # limit=3 取三条
    offset_query_param = 'offset'  # offset=1 从第一个位置开始 取limit条
    max_limit = 5  # 每页最大显示5条
    # offset=3&limit=2    0 1 2 3 4 5   # 取得是3 4


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

# 配置在视图类上即可
from app01.three_auth.page import CommonPageNumberPagination, CommonLimitOffsetPagination, CommonCursorPagination

    # 三种分页
    # pagination_class = CommonPageNumberPagination
    # pagination_class = CommonLimitOffsetPagination
    pagination_class = CommonCursorPagination
    
'''
    # 之前的东西一样用 ,内置的分页类不能直接使用,需要继承,定制一些参数后才能使用
    # pagination_class = PageNumberPagination
    #基本分页方式(基本是这种,网页端):http://127.0.0.1:8000/api/v1/books/?page=2&size=3

    # pagination_class = LimitOffsetPagination
    # 偏移分页 http://127.0.0.1:8000/api/v1/books/?limit=4&offset=1
    # 从第一条开始,取4条

    pagination_class = CommonCursorPagination
    # 游标分页,只能下一页,上一页,不能跳到中间,但它的效率最高,大数据量分页,使用这种较好
'''

posted @ 2023-02-07 21:55  lsumin  阅读(18)  评论(0编辑  收藏  举报