• [Python自学] DRF (5) (解析器、URL控制、分页)


    参考博客:https://www.cnblogs.com/yuanchenqi/articles/8719520.html

    一、数据解析器

    1.Django默认的数据解析

    在我们使用django框架时,浏览器如果发送以下类型的POST数据:

    Content-Type=application/x-www-form-urlencoded
    Content-Type=application/form-data

    则django会自动帮我们将请求体中的数据转换为字典,保存在request.POST中。

    但是,如果浏览器发送的是json数据:

    Content-Type=application/json

    则django无法为我们转换,我们只能从request.body中获取原始数据,自己进行转换。

    2.restframework提供的解析器

    如果我们使用restframework,他给我们提供了几个常用的数据解析器,列表如下:

    from rest_framework import parsers
    
    # 处理json数据,media_type = 'application/json'
    parsers.JSONParser
    # 处理x-www-form-urlencoded数据,media_type = 'application/x-www-form-urlencoded'
    parsers.FormParser
    # 处理multipart/form-data数据,media_type = 'multipart/form-data'
    parsers.MultiPartParser
    # 处理所有数据,media_type = '*/*'
    parsers.FileUploadParser

    如果我们在视图类中不指定需要使用的解析器,则默认会使用三种:

    在APIView类中可以看到:

    class APIView(View):
    
        # The following policies may be set at either globally, or per-view.
        renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
        parser_classes = api_settings.DEFAULT_PARSER_CLASSES
        authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
        ...
        ...

    在DEFAULTS中找到 DEFAULT_PARSER_CLASSES :

    'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser'
    ],

    默认使用了JSONParser、FormParser和MultiPartParser三种解析器。

    3.在视图类中指定可以使用的解析器

    class LoginView(APIView):
        authentication_classes = []
        parser_classes = [parsers.JSONParser,parsers.FormParser]
        ...
        ...

    我们可以在视图类中使用parser_classes列表来指定该视图类可以使用的解析器。

    当然,我们也可以仿造JSONParser等解析器实现自己的解析器。

    二、URL控制

    1.自动生成URL路由条目

    在 [Python自学] restframework (2) (视图类的封装) 中的level-3中,一个视图类提供5种操作,但是要对应两条urls路由条目。

    其实restframework也给我们提供了一个封装,来自动生成相应的urls:

    from django.contrib import admin
    from django.urls import path, re_path, include
    from demo import views
    
    from rest_framework import routers
    
    router = routers.DefaultRouter()
    router.register(r'authors', views.AuthorViewSet)
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        re_path('^publishes/$', views.PublishView.as_view(), name="publish"),
        re_path('^publishes/(?P<pk>d+)/$', views.PublishDetailView.as_view(), name="publishdetail"),
        re_path('^books/$', views.BookView.as_view(), name="book"),
        re_path('^books/(?P<pk>d+)/$', views.BookDetailView.as_view(), name="bookdetail"),
        # re_path('^authors/$', views.AuthorViewSet.as_view({"get": "list", "post": "create"}), name="author"),
        # re_path('^authors/(?P<pk>d+)/$', views.AuthorViewSet.as_view(
        #     {"get": "retrieve", "put": "update", "patch": "partial_update", "delete": "destroy"}), name="authordetail"),
        re_path('^login/$', views.LoginView.as_view(), name="login"),
        re_path('', include(router.urls))
    ]

    这样,就不用自己写两条authors的路由条目了。

    2.测试结果

    1)浏览器访问authors全部数据,http://127.0.0.1:8000/authors/?token=81ba24601ce5be47ff2d96142a8ccb76:

     2)浏览器访问authors全部数据,http://127.0.0.1:8000/authors.json?token=81ba24601ce5be47ff2d96142a8ccb76:

    3)浏览器访问authors全部数据,http://127.0.0.1:8000/authors/?format=json&token=81ba24601ce5be47ff2d96142a8ccb76:

    4)浏览器访问authors中id为1的数据,http://127.0.0.1:8000/authors/1/?token=81ba24601ce5be47ff2d96142a8ccb76:

    3.总结

    使用restframework提供的url控制功能,可以自动为我们生成4条路由条目:

    ^authors/$ [name='author-list']
    ^authors.(?P<format>[a-z0-9]+)/?$ [name='author-list']
    ^authors/(?P<pk>[^/.]+)/$ [name='author-detail']
    ^authors/(?P<pk>[^/.]+).(?P<format>[a-z0-9]+)/?$ [name='author-detail']

    其中,当使用浏览器访问时,authors.json和authors的区别在于是否提供渲染页面。前者只返回纯数据,后者会返回restframework提供的页面。

    三、分页

    1.直接在视图类中添加分页

    在 [Python自学] restframework (1) 中,我们手工从数据库中获取books数据,并序列化返回给浏览器。GET获取全量数据的代码如下:

    class BookModelSerializers(serializers.ModelSerializer):
        class Meta:
            model = Book
            fields = "__all__"
    
    
    class BookView(APIView):
        def get(self, request):
            book_list = Book.objects.all()
            bs = BookModelSerializers(book_list, many=True)
    
            return Response(bs.data)
    
        def post(self, request):
            pass

    如果我们想为其添加分页,每次只返回固定的条数:

    class BookView(APIView):
        def get(self, request):
            book_list = Book.objects.all()
    
            # 添加分页
            from rest_framework.pagination import PageNumberPagination
            pnp = PageNumberPagination()
            # 获取每一页
            book_page = pnp.paginate_queryset(book_list, request, self)
            # 将book_page传入进行序列化
            bs = BookModelSerializers(book_page, many=True, context={'request': request})
    
            return Response(bs.data)

    每页显示的页数,我们可以在settings中的REST_FRAMEWORK中配置:

    REST_FRAMEWORK = {
        # "DEFAULT_AUTHENTICATION_CLASSES": ["demo.utils.TokenAuth"],
        # "DEFAULT_PERMISSION_CLASSES":["demo.utils.SVIPPermission"]
        "PAGE_SIZE": 2,
    }

    测试结果:

    访问 http://127.0.0.1:8000/books/默认显示第一页的两条数据:

     访问 http://127.0.0.1:8000/books/?page=3显示指定页的数据:

    2.自定义分页子类

    在前面我们的PAGE_SIZE是在settings中配置的,我们也可以自定义一个类,继承自 PageNumberPagination类:

    from rest_framework.pagination import PageNumberPagination
    
    
    class MyPageNumberPagination(PageNumberPagination):
        page_size = 1
        # 在url中指定页数的参数名http://127.0.0.1:8000/books/?page=1
        page_query_param = 'page'
        # 在url中指定每页显示条数http://127.0.0.1:8000/books/?page=1&size=2
        page_size_query_param = 'size'
    
    
    class BookView(APIView):
        # authentication_classes = [TokenAuth]
    
        def get(self, request):
            book_list = Book.objects.all()
    
            # 这里使用我们自定义的类
            pnp = MyPageNumberPagination()
            # 获取每一页
            book_page = pnp.paginate_queryset(book_list, request, self)
            # 将book_page传入进行序列化
            bs = BookModelSerializers(book_page, many=True, context={'request': request})
    
            return Response(bs.data)

    这样,我们在自己定义的子类中就可以指定page_size等参数,无需到settings中去配置了。而且这样可以让每个视图类由不同的分页特性。例如可以让books和authors每页显示条数不同。

    3.另外一种分页类(limit和offset)

    除了 PageNumberPagination类,restframe还提供了另一个类 LimitOffsetPagination:

    from rest_framework.pagination import LimitOffsetPagination
    
    
    class MyLimitOffsetPagination(LimitOffsetPagination):
        # 相当于PageNumberPagination类的page_size
        default_limit = 1
        # url中的limit参数,控制每页显示条数
        limit_query_param = 'limit'
        # url中的offset参数,控制偏移多少
        offset_query_param = 'offset'
    
    
    class BookView(APIView):
        # authentication_classes = [TokenAuth]
    
        def get(self, request):
            book_list = Book.objects.all()
    
            # 这里使用我们自定义的类
            pnp = MyLimitOffsetPagination()
            # 获取每一页
            book_page = pnp.paginate_queryset(book_list, request, self)
            # 将book_page传入进行序列化
            bs = BookModelSerializers(book_page, many=True, context={'request': request})
    
            return Response(bs.data)

    使用 MyLimitOffsetPagination 类的话,访问的url会不同,例如:

     limit=2表示每页显示两条数据(默认设置的是1条)。

    offset=1表示向后偏一条数据(注意,是一条数据,不加offset=1时,显示的是id为8和9的数据,加了offset=1显示的时id为9和10的数据)。

    4.封装后的视图类如何使用分页类

    [Python自学] restframework (2) (视图类的封装)中,我们对视图类的封装有三个级别,以最后level-3作为例子,如何使用分页器:

    from rest_framework.pagination import PageNumberPagination
    
    
    class MyPageNumberPagination(PageNumberPagination):
        page_size = 1
        page_query_param = 'page'
        page_size_query_param = 'size'
    
    
    # Author序列化类
    class AuthorModelSerializers(serializers.ModelSerializer):
        class Meta:
            model = Author
            fields = "__all__"
    
    
    class AuthorViewSet(viewsets.ModelViewSet):
        queryset = Author.objects.all()
        serializer_class = AuthorModelSerializers
        pagination_class = MyPageNumberPagination

    这样设置以后,restframe就会自动的去使用 MyPageNumberPagination 类进行分页操作了。具体流程可以跟踪源代码。原理类似认证、权限、频率组件。

    f≥Ö‿Ö≤

  • 相关阅读:
    SpringCloud微服务Zuul跨域问题
    com.netflix.zuul.exception.ZuulException: Hystrix Readed time out
    Java实现遍历N级树形目录结构
    ubuntu安装Nginx
    redis报错:java.net.SocketException: Broken pipe (Write failed); nested exception is redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: Broken pipe (Write failed)
    Java设置接口跨域
    SpringBoot使用qq邮箱发送邮件
    linux使用Nginx搭建静态资源服务器
    Spring Boot 正常启动后访问Controller提示404
    分享2019年陆陆续续读过的书-附书单
  • 原文地址:https://www.cnblogs.com/leokale-zz/p/12238449.html
Copyright © 2020-2023  润新知