认证组件

以后,有的接口需要登录后才能访问,有的接口,不登录就能访问

写一个登录接口,返回token,以后只要带着token过来,就是登录了 不带 就没有登录


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

认证组件使用步骤

1.写一个认证类,继承BaseAuthentication
2.重写authenticate方法,在该方法中实现登录认证:token在哪带的?如果认证它就是登录了
      从地址栏中取request.query_params.get('token')
        原生django,取出前端传入cookie,request.COOKIE.get('sessionid')
        后期如果想从请求头中取 request.META.get('HTTP_TOKEN')
3.如果认证成功,返回两个值【返回None或两个值】第一个是token,或者返回None
4.认证不通过,抛异常AuthenticationFailed
5.局部使用和全局使用
    局部:只在某个视图类中使用【当前视图类管理的所有接口】
	from rest_framework.viewsets import ViewSetMixin
	from rest_framework.generics import ListAPIView, RetrieveAPIView
	from .authenticate import LoginAuth
	class BookDetailView(ViewSetMixin, RetrieveAPIView):
		queryset = Book.objects.all()
		serializer_class = BookSerializer
		authentication_classes = [LoginAuth]

     全局:全局所有接口都生效(登录接口不要)
	REST_FRAMEWORK = {'DEFAULT_AUTHENTICATION_CLASSES':['app01.authenticate.LoginAuth']
}
      
     局部禁用:
	class BookDetailView(ViewSetMixin, RetrieveAPIView):
            authentication_classes = []

代码

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


# 查询所有
class BookView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    # authentication_classes = []  #  这个是局部禁用


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


认证类代码
from rest_framework.authentication import BaseAuthentication
from .models import UserToken
from rest_framework.exceptions import AuthenticationFailed
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没传')


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

router = SimpleRouter()

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

urlpatterns = [
    path('admin/', admin.site.urls),

    path('api/v1/', include(router.urls)),
]

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

权限组件

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

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

查询单个查询所有都要登录才能访问 ---》全局认证
    查询单个需要超级管理员才能访问
    查询所有,所有登录用户都能访问

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

权限的使用

1.写一个权限类,继承BasePermission
2.重写has_permission方法,在该方法中实现权限认证,在这个方法中,request.user就是当前登录用户
        ACL:访问控制列表
        rbac:公司内部系统,基于角色的访问控制
        abac:rbac升级版,加了属性认证
3.如果有权限,返回True
4.没有权限,返回False,定制返回的中文:self.message='中文'
5.局部使用和全局使用
    局部:只在某个视图类中使用【当前视图类管理的所有接口】
      class BookDetailView(ViewSetMixin, RetrieveAPIView):
          permission_classes = [CommonPermission]

    全局:全局所有接口都生效
         REST_FRAMEWORK = {
            'DEFAULT_PERMISSION_CLASSES': ['app01.permissions.CommonPermission',]}

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

代码

需要增加一个user_type字段
权限类
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
            self.message = '您是【%s】 您没有权限' % request.user.get_user_type_display()  # 这有一个choices属性的一个对应关系  需要记忆 后期会用
            return False

视图代码
from .permissions import CommonPermission
class BookDetailView(ViewSetMixin, RetrieveAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    permission_classes = [CommonPermission]  # 权限类 其他和认证代码一致 局部和全局配置必须有一个 执行顺序局部优先

频率组件

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

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

使用步骤

1.写一个频率类 继承SimpleRateThrottle
2.重写get_cache_key方法,返回什么就以什么限制---》ip 用户id做限制
3.配置一个类属性:scope = 'book_5_m'
4.在配置文件中配置
      'DEFAULT_THROTTLE_RATES': {
        '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 = 'lqz'

    def get_cache_key(self, request, view):
        # 返回什么就以什么做频率限制【可以返回ip 或用户ID】、
        # 客户端的ip地址 从request.META.get('REMOTE_ADDR')

        return request.user.pk  # 以用户id做限制

配置文件中的配置和属性scope有关
    # 全局 频率配置
    # 'DEFAULT_THROTTLE_CLASSES': ['app01.throttling.CommonThrottle'],
    'DEFAULT_THROTTLE_RATES': {
        'lqz': '5/m',
    },

视图类代码
from .throttling import CommonThrottle
class BookDetailView(ViewSetMixin, RetrieveAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    throttle_classes = [CommonThrottle]

过滤排序

restful规范中 要求了 请求地址中带过滤条件
    5个接口中,只有一个接口需要有过滤和排序 查询所有接口
     
     3种过滤方式(1.内置的:模糊匹配,2.第三方需要下载django—filter模块:精准匹配,3.自定义过滤类)


查询 所有图书接口 查询以  红 开头的所有图书

继承APIView 伪代码

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

继承APIView如何写,完全自己写,麻烦,但是清晰

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

'''
注意:过滤使用时前端的数据格式里面的字段都是根据序列化类里面的字段进行组合和反序列化的
eg:
   查询的时候没有id字段  在字段里加上就有了
'''

2.使用第三方django—filter实现过滤

安装django-filter
pip3.8 install django-filter -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com

'''
注意:django版本会自动升级为最新版本  需要修改回来
'''
再执行一下

pip3.8 install django==2.2.2 -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com

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=万历十五年&price=444


支持的查询方式(完整查询)
http://127.0.0.1:8000/api/v1/books/?price=939
http://127.0.0.1:8000/api/v1/books/?price=22&name=红楼梦

3.自己定制过滤类实现过滤

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


第一步; 定义一个过滤类,继承BaseFilterBackend,重写filter_queryset方法
from rest_framework.filters import BaseFilterBackend
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 .filter import CommonFilter
class BookView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [CommonFilter]  # 可以定制多个,从左往右,依次执行



自己定制的类 在视图类下可以添加另外两种过滤方式(SearchFilter,DjangoFilterBackend)的编写 但是要加上过滤的字段 
内置过滤类 : search_fields = ['name', 'price']   # 可以按名字模糊匹配
第三方过滤类:filterset_fields = ['name', 'price']  # 支持完整匹配,

排序的使用

在过滤的基础上加上排序
from rest_framework.filters import SearchFilter,OrderingFilter
from .filter import CommonFilter
# 查询所有
class BookView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    filter_backends = [CommonFilter,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

分页

分页 只有查询所有接口 才有分页 (必须继承GenericAPIView才有分页类)

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条


# LimitOffset
class CommonLimitOffsetPagination(LimitOffsetPagination):
    default_limit = 3  # 每页显示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 5


# app 用下面
class CommonCursorPagination(CursorPagination):
    cursor_query_param = 'cursor'  # 查询参数
    page_size = 2  # 每页多少条
    ordering = 'id'  # 排序字段

配置在视图类上即可

from .page import CommonPageNumberPagination as PageNumberPagination
from .page import CommonLimitOffsetPagination as LimitOffsetPagination
from .page import CommonCursorPagination

class BookView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    permission_classes = []
    authentication_classes = []
    throttle_classes = []
    # 之前的东西一样用 ,内置的分页类不能直接使用,需要继承,定制一些参数后才能使用
    # 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
    # 游标分页,只能下一页,上一页,不能跳到中间,但它的效率最高,大数据量分页,使用这种较好