| 目录 
 一、认证组件
 
 简介:
 
 (1)、登录接口
 登录认证的限制
认证组件是drf框架给我们提供的认证接口,它能够在请求进入视图函数/类前进验证(例如:认证用户是否登录),对不符合认证的请求进行拦截并返回校验失败的信息
 
 复制代码# 认证是基于登录的接口上面操作的 所以前戏编写一个简单的登录接口models.pyclass User(models.Model):  # 简易的用户信息账号密码    username = models.CharField(max_length=32)    password = models.CharField(max_length=32)    def __str__(self):        return self.username        '跟User表是一对一外键关联,存储用户登录状态用的 [这个表可以没有,如果没有,把字段直接写在User表上也可以]'class UserToken(models.Model):  # 用户信息登录记录表    user = models.OneToOneField(to='User', on_delete=models.CASCADE)  # 一对一关联    token = models.CharField(max_length=32, null=True)  # 如果用户没有登录则没有值 如果登录则有值views.py        '登录接口功能:自动生成路由+登录功能,不用序列化,因此继承ViewSet即可'class UserView(ViewSet):    @action(methods=['POST'], detail=False, url_path='login', url_name='login')    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())              # uuid4 随机获得永不重复的字符串 机制跟Cookie中的验证码一样            # 在userToken表中存储一下:1 从来没有登录过,插入一条,     2 登录过,修改记录                                    UserToken.objects.update_or_create(defaults={'token': token}, user=user)             # 通过user去UserToken表中查数据,如果能查到,使用defaults的数据更新,如果查不到,直接通过user和defaults的数据新增            # kwargs 传入的东西查找,能找到,使用defaults的更新,否则新增一条            return Response({'code': 100, 'msg': '登录成功', 'token': token})        else:            return Response({'code': 101, 'msg': '用户名或密码错误'})urls.py        from rest_framework.routers import SimpleRouter, DefaultRouter        router = SimpleRouter()        router.register('users', views.UserView, 'users')        urlpatterns += router.urls'''这个时候一个简单的登录接口就写好了 每次登录都会更新Token 相当于登录了之前的设备就无效了 '''
 update_or_create源码如下:
 (2)、认证组件使用步骤复制代码    def update_or_create(self, defaults=None, **kwargs):        defaults = defaults or {}        self._for_write = True        with transaction.atomic(using=self.db):            try:                obj = self.select_for_update().get(**kwargs)            except self.model.DoesNotExist:                params = self._extract_model_params(defaults, **kwargs)                obj, created = self._create_object_from_params(kwargs, params, lock=True)                if created:                    return obj, created            for k, v in defaults.items():                setattr(obj, k, v() if callable(v) else v)            obj.save(using=self.db)        return obj, False
 
 1.需要写一个认证类,因此我们需要在应用中另外创建一个py文件编写认证类,需要继承BaseAuthentication这个类
 
 
 通过查看源码我们可以发现有个authenticate方法需要我们重写,否则就会报错,这就是我们需要编写认证功能的类
 复制代码class BaseAuthentication:    def authenticate(self, request):        raise NotImplementedError(".authenticate() must be overridden.")    def authenticate_header(self, request):        pass
2.重写authenticate方法,在该方法在中实现登录认证
 
 
 token在哪带的?如何认证它是登录了的?
用token来判断是否登陆,登陆了在访问的时候带上token,目前阶段我们直接在地址栏中携带token的数据,后面可以在请求头中添加token的数据
 3、如果认证成功,返回两个值【返回None或两个值(固定的:当前登录用户,token)】
 4、认证不通过,用AuthenticationFailed类抛异常代码如下:
 authenticate.py(认证类)
 复制代码# 自己写的认证类,继承某个类from rest_framework.authentication import BaseAuthenticationfrom rest_framework.exceptions import AuthenticationFailedfrom .models import UserTokenclass LoginAuth(BaseAuthentication):    def authenticate(self, request):        # 在这里实现认证,如果是登录的,继续往后走返回两个值,如果不是抛异常        # 请求中是否携带token,判断是否登录,放在地址栏中        token = request.query_params.get('token', None) # 查找是否有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没传')# 前端传入的请求头中的数据从哪取?  GET,body,POST,data
5、认证类的使用
 
 
 当我们编写好了认证类中的认证代码,接着就需要导入到视图层然后使用他
 复制代码from rest_framework.generics import ListAPIView, RetrieveAPIViewfrom rest_framework.viewsets import ViewSetMixinfrom .authenticate import LoginAuth# 查询所有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]  # 需要写一个认证类,需要咱们自行编写
6、局部使用和全局使用
 
 
 局部使用:只在某个视图类中使用【当前视图类管理的所有接口】
 复制代码class BookDetailView(ViewSetMixin, RetrieveAPIView):        authentication_classes = [LoginAuth]
 全局使用:在配置文件settings.py中编写,全局所有接口都生效
 注意事项:不要在配置文件中乱导入不使用的东西,否则会报错,但是在导入类似认证类这样的文件时,可以写上导入的代码然后再修改,最后写进配置中,这样可以减少错误复制代码REST_FRAMEWORK = {            'DEFAULT_AUTHENTICATION_CLASSES':['app01.authenticate.LoginAuth']        }
 
 局部禁用:(登陆接口很明显是不需要校验是否登陆的,因此有了这个局部禁用的需求,我们把他的authentication_classes配置成空就是局部禁用)
 复制代码class BookDetailView(ViewSetMixin, RetrieveAPIView):        authentication_classes = []
7、测试路由参考
  (3)、整体代码
 
 views.py
 authenticate.py(认证类)复制代码# 查询所有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]  # 需要写一个认证类,需要咱们自行编写
urls.py复制代码# 自己写的认证类,继承某个类from rest_framework.authentication import BaseAuthenticationfrom rest_framework.exceptions import AuthenticationFailedfrom .models import UserTokenclass LoginAuth(BaseAuthentication):    def authenticate(self, request):        # 在这里实现认证,如果是登录的,继续往后走返回两个值,如果不是抛异常        # 请求中是否携带token,判断是否登录,放在地址栏中        token = request.query_params.get('token', None) # 查找是否有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没传')# 前端传入的请求头中的数据从哪取?  GET,body,POST,data
权限组件复制代码from django.contrib import adminfrom django.urls import path, includefrom app01 import viewsfrom rest_framework.routers import SimpleRouterrouter = 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)),]
 简介:
 
 (1)、权限组件的使用步骤
 在我们使用的一些app或者网页中(爱奇艺,腾讯视频),都会有一些会员接口(需要购买会员才能够使用或者观看),权限组件就是对用户的这一权限进行验证,在请求进入视图类/函数代码前进行校验,校验失败后直接将请求拦截,并返回校验失败的信息
 
 模块地址:
 用法简介:复制代码from rest_framework.permissions import BasePermission
(2)、代码用法复制代码# 1、创建一个专门用于编写权限组件的py文件,写一个权限类,继承BasePermission# 2、重写has_permission方法(在该方法在中实现权限认证,在这方法中,request.user就是当前登录用户)# 3、如果有权限,返回True# 4、没有权限,返回False(定制返回的中文: self.message='中文')# 5、局部使用和全局使用        -局部使用: # 在某个视图类中设置接口(不会影响别的视图类)        class BookDetailView(ViewSetMixin, RetrieveAPIView):            permission_classes = [CommonPermission]        -全局使用:  # django的settings.py中配置,影响全局            REST_FRAMEWORK = {            'DEFAULT_PERMISSION_CLASSES': [                'app01.permissions.CommonPermission',            ],        }          -局部禁用:# 全局配置局部禁用        class BookDetailView(ViewSetMixin, RetrieveAPIView):            permission_classes = [] 
 models.py(修改User表的配置后需要重新进行数据库迁移)
 perssion.py(权限类代码)复制代码class User(models.Model):    username = models.CharField(max_length=32)    password = models.CharField(max_length=32)    user_type = models.IntegerField(choices=((1, '超级管理员'), (2, '普通用户'), (3, '2B用户')), default=2)
views.py(视图类代码)复制代码# 写权限类,写一个类,继承基类BasePermission,重写has_permission方法,在方法中实现权限认证,如果有权限return True ,如果没有权限,返回Falsefrom rest_framework.permissions import BasePermissionclass 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
复制代码class BookView(ModelViewSet):    queryset = Book.objects.all()    serializer_class = BookSerializer    # 局部认证    authentication_classes = [LoginAuth]    # 权限认证(将编写的频率类导入过来)    permission_classes = [CommentPermission]
 三、频率组件
 
 简介:
 (1)、频率组件的使用步骤
 
 模块地址:
 用法简介复制代码from rest_framework.throttling import BaseThrottle, SimpleRateThrottle# BaseThrottle:需要手动编写的代码较多# SimpleRateThrottle: 需要手动编写的代码较少(用这个)
(2)、代码用法复制代码# 1、创建一个专门用来编写频率组件的py文件,写一个频率类,继承SimpleRateThrottle# 2、重写get_cache_key方法,返回什么,就以什么做限制----》ip(用户id做限制)# 3、配置一个类属性scope = 'book_5_m'# 4、在django的配置文件中编写频率次数        REST_FRAMEWORK = {          'DEFAULT_THROTTLE_RATES': {            'book_5_m': '5/m',  # 一分钟五次         },            }# 5、局部使用和全局使用        -局部使用: # 只影响当前的视图类    class BookView(ModelViewSet):        throttle_classes = [CommentThrottle]         -全局配置:影响全局            REST_FRAMEWORK = {             'DEFAULT_THROTTLE_CLASSES': ['app01.throttling.CommonThrottle'],        }         -局部禁用:      class BookView(ModelViewSet):         throttle_classes = [CommentThrottle]
 throttle.py(频率类代码)
 视图类代码复制代码from rest_framework.throttling import BaseThrottle, SimpleRateThrottleclass CommentThrottle(SimpleRateThrottle):    # 创建一个用于控制频率的变量名(需要传入配置文件)    scope = 'book_5_m'    def get_cache_key(self, request, view):        # 返回什么就以什么做限制(request.META.get('REMOTE_ADDR')以IP做限制)        return request.META.get('REMOTE_ADDR')
django配置文件复制代码class BookView(ModelViewSet):    queryset = Book.objects.all()    serializer_class = BookSerializer    # 频率组件    throttle_classes = [CommentThrottle]
复制代码REST_FRAMEWORK = {    # 控制访问频率    'DEFAULT_THROTTLE_RATES': {        'book_5_m': '5/m',  # 一分钟五次    },}
 四、过滤的多种用法
 
 简介:
 
 
 过滤是指在使用查询的时候,我们可以通过条件来过滤掉不需要的内容
 (1)、继承APIView自己写复制代码# restful规范中,要求了,请求地址中带过滤条件        -5个接口中,只有一个接口需要有过滤和排序,查询所有接口
 (2)、使用drf的内置过滤(继承GenericAPIview)复制代码class BookView(APIView):    def get(self,request):        # 获取get请求携带的参数        name=request.query_params.get('name')        # 通过filter进行过滤        books = Book.objects.filter(name=name)
 模块地址:
 该方法为模糊查询
 代码用法:复制代码from rest_framework.filters import SearchFilter
搜索方式:复制代码class BookView(ModelViewSet):    queryset = Book.objects.all()    serializer_class = BookSerializer    # 实例化过滤对象    filter_backends = [SearchFilter]    # 指定过滤的字段(模糊查询)    search_fields = ['name', 'price']
复制代码# name或price中只要有关键字就会搜出来 (只能用search=xxx的方式)        http://127.0.0.1:8000/api/v1/books/?search=西游记
 (3)、使用第三方插件过滤(精准过滤)
 
 第三方插件:
 模块地址:复制代码# 插件名称:        django-filter    # 安装插件:        pip3.8 install django-filter
代码用法:复制代码from django_filters.rest_framework import DjangoFilterBackend
搜索方式:复制代码from rest_framework.viewsets import ModelViewSetfrom .models import Bookfrom .serializer import BookSerializerfrom django_filters.rest_framework import DjangoFilterBackendclass BookView(ModelViewSet):    queryset = Book.objects.all()    serializer_class = BookSerializer    # 第三方过滤插件    filter_backends = [DjangoFilterBackend]    # 查询的字段    filterset_fields = ['pk','name', 'price']
复制代码http://127.0.0.1:8000/api/books/?price=99http://127.0.0.1:8000/api/books/?price=99&name=呐喊
 4、使用过滤组件
 
 
 1.定制过滤组件的使用方式与步骤模块地址:
 用法简介:复制代码from rest_framework.filters import BaseFilterBackend
复制代码# 1、创建一个专门用于过滤的py文件,写一个类继承BaseFilterBackend# 2、重写filter_queryset方法,在方法内部进行过滤# 3、直接返回过滤后的对象# 4、如果没有过滤直接返回所有数据# 5、局部使用         -局部使用:    class BookView(ViewSetMixin, ListAPIView):        queryset = Book.objects.all()        serializer_class = BookSerializer        # 在类表内填写过滤的类        filter_backends = [CommonFilter]  # 可以定制多个,从左往右,依次执行
2.代码用法过滤类代码filters.py
 视图类代码复制代码from rest_framework.filters import BaseFilterBackendclass CommonFilter(BaseFilterBackend):    # 重写的类,编写过滤吧的内容    def filter_queryset(self, request, queryset, view):        # 获取过滤的条件        filter_comment = request.query_params.get('price_gt', None)        # 判断前端是否传入过滤条件        if filter_comment:            # 根据条件进行赛选内容            books_queryset_filter = queryset.filter(price__gt=100)            # 返回过滤后的数据            return books_queryset_filter        return queryset
五、排序的使用复制代码class BookView(ModelViewSet):    queryset = Book.objects.all()    serializer_class = BookSerializer    # 第三方过滤插件    filter_backends = [CommonFilter]
 用法简介
 
 
 排序需要和自定义过滤继承同一个父类,需要将排序的对象填入在过滤的列表内,并且放在其他参数的前方模块地址:
 
 (2)、代码用法复制代码from rest_framework.filters import OrderingFilter
 视图类代码
 搜索用法复制代码from rest_framework.viewsets import ModelViewSetfrom .models import Bookfrom .serializer import BookSerializerfrom django_filters.rest_framework import DjangoFilterBackendfrom .filters import CommonFilterfrom rest_framework.filters import OrderingFilterclass BookView(ModelViewSet):    queryset = Book.objects.all()    serializer_class = BookSerializer    # 第三方过滤插件(OrderingFilter放在其他过滤参数前)    filter_backends = [OrderingFilter, DjangoFilterBackend, CommonFilter]    # 查询的字段    filterset_fields = ['id', 'name', 'price']    # 指定排序的字段(-是降序,默认升序)    ordering_fields = ['price']
六、分页复制代码# 默认升序    http://127.0.0.1:8000/api/books/?price_gt=60&ordering=price    # 降序        http://127.0.0.1:8000/api/books/?price_gt=60&ordering=-price
 
 使用步骤
 分页功能,只有查询所有接口,才有分页
drf内置了三个分页器,对应三种分页方式
内置的分页类不能直接使用,需要继承,定制一些参数后才能使用
 
 代码
 步骤一:创建一个py文件编写分页用到的自定义类,分页的三个类并不能直接使用,需要我们进行配置
步骤二:编写这个自定义类
步骤三:导入视图类中,并添加配置
 page.py(自定义的分页类)
 views.py复制代码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条'''page_size 每页数目page_query_param 前端发送的页数关键字名,默认为”page”page_size_query_param 前端发送的每页数目关键字名,默认为Nonemax_page_size 前端最多能设置的每页数量'''        # LimitOffsetclass 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'''default_limit 默认限制,默认值与PAGE_SIZE设置一直limit_query_param limit参数名,默认’limit’offset_query_param offset参数名,默认’offset’max_limit 最大limit限制,默认None'''# app 用下面class CommonCursorPagination(CursorPagination):    cursor_query_param = 'cursor'  # 查询参数    page_size = 2  # 每页多少条    ordering = 'id'  # 排序字段'''cursor_query_param:默认查询字段,不需要修改page_size:每页数目ordering:按什么排序,需要指定'''
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!复制代码#  分页功能   必须是继承GenericAPIView ,如果是继承APIView,要自己写(你写)from .page import CommonPageNumberPagination as PageNumberPaginationfrom .page import CommonLimitOffsetPagination as LimitOffsetPaginationfrom .page import CommonCursorPagination as CommonCursorPaginationclass 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    # 游标分页,只能下一页,上一页,不能跳到中间,但它的效率最高,大数据量分页,使用这种较好
 |