最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

消息!drf认证/权限/频率/分页-过滤-排序

来源:博客园


(资料图)

内容概要

  • 路由系统

  • 认证组件

  • 权限组件

  • 频率组件

  • 过滤/排序

  • 分页组件

路由系统

1.自动生成路由

drf 由于继承ViewSetMixin类,路由写法变了-原生+drf,以后的路由写法,可能会有如下情况(三种情况)    1.-path("books/", views.BookView.as_view()        2.-path("books/", views.BookView.as_view({"get": "list", "post": "create"}))        3.-自动生成# drf提供了两个路由类,继承ModelViewSet后,路由可以自动生成              # 使用步骤:    # 第一步:导入路由类         from rest_framework.routers import SimpleRouter,DefaultRouter    # 第二步,实例化得到对象(两个类,一般使用SimpleRouter)                router = SimpleRouter()    # 第三步:注册:router.register("books", views.BookView, "books")    # 第四步:在urlpatterns中注册,两种方式        1.-urlpatterns += router.urls        2.-include:path("/api/v1/", include(router.urls))  方式多一些                                        # 底层实现:自动生成路由就       -本质是自动做映射,能够自动成的前提是,视图类中要有 5个方法的某几个或多个           get--->list           get---->retrieve           put---->update           post---->create           delete---->destory                       -ModelViewSet,ReadOnlyModelViewSet可以自动生成                     -9个试图子类+配合ViewSetMixin   才可以自动生成                       -GenericAPIView+5个试图扩展类+配合ViewSetMixin   才能自动生成                

2.action装饰器

# action 装饰器是写在视图类的方法上,可以自动生成路由# 使用步骤1.写在视图类方法上    class SendView(ViewSet)    # methods指定请求方法,可以传多个        # detail:只能传True和False        -False,不带id的路径:send/send_sms/            -True,带id的路径:send/2/send_sms/        #url_path:生成send后路径的名字,默认以方法名命名         # url_name:别名,反向解析使用,了解即可        @action(methods=["POST"], detail=False)        def send_sms(self, request):# 以后看到的drf路由写法后期,都是自动生成,一般不在urlpatterns 加入路由了          # action 写在视图类的方法上,可以自动生成路由# 使用步骤- 1 写在视图类方法上    class SendView(ViewSet):        # methods指定请求方法,可以传多个        # detail:只能传True和False        -False,不带id的路径:send/send_sms/            -True,带id的路径:send/2/send_sms/        # url_path:生成send后路径的名字,默认以方法名命名         # url_name:别名,反向解析使用,了解即可        @action(methods=["POST"], detail=False)        def send_sms(self, request):                         # 以后看到的drf路由写法后期,都是自动生成,一般不在urlpatterns 加入路由了     # 补充:-1 不同请求方式可以使用不同序列化类    -2 不同action使用不同序列化类class SendView(GenericViewSet):    queryset = None    serializer_class = "序列化类"    def get_serializer(self, *args, **kwargs):        if self.action=="lqz":            return "某个序列化类"        else:            return "另一个序列化列"    @action(methods=["GET"], detail=True)    def send_sms(self, request,pk):        print(pk)        # 手机号,从哪去,假设get请求,携带了参数        phone = request.query_params.get("phone")        print("发送成功,%s" % phone)        return Response({"code": 100, "msg": "发送成功"})    @action(methods=["GET"], detail=True)    def lqz(self,request):  # get        # 序列化类        pass    @action(methods=["GET"], detail=True)    def login(self,request):  # get        # 序列化类        pass

认证组件

1.认证组件使用步骤
# 1 写一个认证类,继承BaseAuthentication# 2 重写authenticate方法,在该方法在中实现登录认证:token在哪带的?如果认证它是登录了# 3 如果认证成功,返回两个值【返回None或两个值】# 4 认证不通过,抛异常AuthenticationFailed# 5 局部使用和全局使用-局部:只在某个视图类中使用(当前视图类管理的的所有接口)            class BookDetailView(ViewSetMixin, RetrieveAPIView):            authentication_classes = [LoginAuth]     -全局:全局所有接口都生效(登录接口不要)    REST_FRAMEWORK = {        "DEFAULT_AUTHENTICATION_CLASSES":["app01.authenticate.LoginAuth"]}    -局部禁用:        class BookDetailView(ViewSetMixin,RetrieveAPIView):            authentication_clsses = []
# 视图类# 查询所有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]  # 需要写一个认证类,需要咱们写# 认证组件 单独的authenticate.py文件编写from rest_framework.authentication import BaseAuthenticationfrom rest_framework.exceptions import AuthenticationFailedfrom app01 import modelsclass LoginAuth(BaseAuthentication):    def authenticate(self, request):        # 在这里实现认证,如果是登录的,继续往后走返回两个值,如果不是抛异常        # 请求中是否携带token,判断是否登录,放在地址栏中        token = request.query_params("token", None)        if token:  # 前端传入token了,去表中查,如果能查到,登录了,返回两个值[固定的:当前登录用户,token]            user_token_obj = models.UserToken.objects.filter(token=token).first()            if user_token_obj:                return user_token_obj.user, token  # 返回两个值[固定的:当前登录用户,token]            # 没有登录抛异常            raise AuthenticationFailed("token验证失败")        raise AuthenticationFailed("没有token")        # 路由代码router.register("books", views.BookView, "books")router.register("books", views.BookDetailView, "books")

权限组件

# 即便登录成功了,有些接口,还是不能访问,因为没有权限# 登录后,有的接口有权限访问,有的没有权限访问# 查询单个和查询所有,都要登录才能访问----》全局认证-查询单个需要超级管理员才能访问    -查询所有,所有登录用户都能访问        # 权限是一个字段,在User表中,加入user_type字段

1.权限使用

# 1 写一个权限类,继承BasePermission# 2 重写has_permission方法,在该方法在中实现权限认证,在这方法中,request.user就是当前登录用户# 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 = [] 

频率组件

# 控制某个接口访问频率(次数)# 查询所有接口,同一个ip一分钟只能访问5次

1.使用步骤

# 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 = [] 

过滤排序

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

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

# 内置过滤类,必须继承GenericAPIView及其子类,才能使用这种方法---》配置过滤类的方式from rest_framework.filters import SearchFilter, OrderingFilterclass BookView(ViewSetMixin, ListAPIView):    queryset = Book.objects.all()    serializer_class = BookSerializer#  SearchFilter内置的,固定用法,模糊匹配#  就有过滤功能了,指定按哪个字段过滤filter_backends = [SearchFilter]# search_fields = ["name"]  # 可以按名字模糊匹配search_fields = ["name","price"]  # 可以按名字模糊匹配或价格模糊匹配

自定义过滤类实现过滤

# 查询价格大于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 import filterclass BookView(ViewSetMixin, ListAPIView):    queryset = Book.objects.all()    serializer_class = BookSerializer    filter_backends = [filter.CommonFilter]  # 可以定制多个,从左往右,依次执行

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

# 安装:django-filter# 下载了django-filter需要重新安装以下django2.22版本 他会自动升级django至4.x版本from django_filters.rest_framework import DjangoFilterBackendclass BookView(ViewSetMixin, ListAPIView):    queryset = Book.objects.all()    serializer_class = BookSerializer    permission_classes = []    authentication_classes = []    throttle_classes = []    filter_backends = [DjangoFilterBackend]    filterset_fields = ["name","price"]  # 支持完整匹配  name=聊斋11&price=933         # 支持的查询方式http://127.0.0.1:8000/api/v1/books/?price=939      

排序使用

# 内置的就够了class BookView(ViewSetMixin, ListAPIView):    queryset = Book.objects.all()    serializer_class = BookSerializer    filter_backends = [OrderingFilter]      ordering_fields = ["price"]    # filter_backends = [OrderingFilter,filter.CommonFilter]  # 可以定制多个,从左往右,依次执行    # 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 

分页使用

#  分页,只有查询所有接口,才有分页# drf内置了三个分页器,对应三种分页方式#内置的分页类不能直接使用,需要继承,定制一些参数后才能使用

自定义一个分页类

# 分页使用,自定义一个分页类(三种)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"  # 排序字段# 配置在视图类上即可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    # 游标分页,只能下一页,上一页,不能跳到中间,但它的效率最高,大数据量分页,使用这种较好

基于APIView编写分页

# 分页功能,只有查询所有才有class BookView(ViewSetMixin, APIView):    def list(self, request):        books = Book.objects.all()        # 使用步骤        # 1 实例化得到一个分页类的对象        paginator = CommonLimitOffsetPagination()        # 2 调用分页类对象的paginate_queryset方法来完成分页,返回的page是 要序列化的数据,分页好的        page = paginator.paginate_queryset(books, request, self)        if page is not None:            serializer = BookSerializer(instance=page, many=True)            # 3 返回数据,调用paginator的get_paginated_response方法            # return paginator.get_paginated_response(serializer.data)            return Response({                "total": paginator.count,                "next": paginator.get_next_link(),                "previous": paginator.get_previous_link(),                "results": serializer.data            })

异常处理

# APIView--->dispatch--->三大认证,视图类的方法,如果出了一场,会被一场捕获,捕获后统一处理# drf 内置了一个函数,只要上面过程出了异常,就会执行这个函数,这个函数只处理的drf的异常-主动抛的非drf异常    -程序出错了     都不会被处理    我们的目标,无论主动抛还是程序运行出错,都同意返回规定格式--》能记录日志    公司里一般返回   {code:999,"msg":"系统错误,请联系系统管理员"}            # 写一个函数,内部处理异常,在配置文件中配置一下即可def common_exception_handler(exc, context):    # exc 错误对象    # context:上下文,有view:当前出错的视图类的对象,args和kwargs视图类方法分组出来的参数,request:当次请求的request对象    # 只要走到这里,就要记录日志 ,只有错了,才会执行这个函数    # 记录日志尽量详细    print("时间,登录用户id,用户ip,请求方式,请求地址,执行的视图类,错误原因")    res = exception_handler(exc, context)    if res:  # 有值,说明返回了Response 对象,没有值说明返回None        # 如果是Response 对象说明是drf的异常,已经被处理了,如果是None表明没有处理,就是非drf的异常        res = Response(data={"code": 888, "msg": res.data.get("detail", "请联系系统管理员")})    else:        # res = Response(data={"code": 999, "msg": str(exc)})        # 记录日志        res = Response(data={"code": 999, "msg": "系统错误,请联系系统管理员"})    return res# 在配置文件中配置REST_FRAMEWORK = {"EXCEPTION_HANDLER": "app01.exceptions.common_exception_handler",}

关键词: 记录日志 配置文件 系统管理员