• 斑马斑马-22-Django REST Framework (DRF)系列教程


    一、APIView

    1、APIView 和 View

    APIView 是DRF概念体系中最基本类视图,也是所有视图的基类,继承自Django的 View 父类。

    APIView 与 View 的不同之处在于:

    1. 传入到视图方法中的是REST framework的 Request 对象,而不是Django的 HttpRequeset 对象;
    2. 视图方法可以返回REST framework的 Response 对象,视图会为响应数据设置 render 符合前端要求的格式;
    3. 任何 APIException 异常都会被捕获到,并且处理成合适的响应信息;
    4. 在进行 dispatch() 分发前,会对请求进行身份认证、权限检查、流量控制

    支持定义的属性:

    1. authentication_classes 列表或元祖,身份认证类
    2. permissoin_classes 列表或元祖,权限检查类
    3. throttle_classes 列表或元祖,流量控制类

    在 APIView 中仍以常规的类视图定义方法来实现 get() 、 post() 或者其他请求方式的方法。

    1. Request
      1. GET:request.query_params
      2. POST:request.data
    2. Response
      1. Response([{},{}],status=404)

    2、通过APIView完善代码 

    from APP01.models import BookInfo, HeroInfo
    from APP01.serializer import BookInfoSerializer, HeroInfoSerializer
    from django.shortcuts import render, HttpResponse
    from django.views import View
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework import status
    from datetime import datetime
    from django.http import JsonResponse
    import json
    
    '''
    功能            请求方式    请求路径    
    获取所有书籍    GET         /books
    创建单本书籍    POST        /books
    获取单本书籍    GET         /books/{pk}
    修改单本书籍    PUT         /books/{pk}
    删除单本书籍    DELETE      /books/{pk}
    '''
    
    
    class PageNumberPaginator():
        def __init__(self, request):
            self.page = int(request.query_params.get("page", 1))
            self.page_size = int(request.query_params.get("page_size", 5))
    
        def paginate_queryset(self, ModelList):
            ind = (self.page - 1) * self.page_size
            end = (self.page) * self.page_size
            return ModelList[ind:end]
    
    
    # 1,列表视图
    class BooksAPIView(APIView):
        '''
        查询所有屠苏,增加图书
        '''
    
        def get(self, request):
            """
            查询所有图书
            路由:GET /books/info?page=1&page_size=5
            """
            pnp = PageNumberPaginator(request)
            book_list = BookInfo.objects.all()
            book_list = pnp.paginate_queryset(book_list)
            # 2:创建可以序列化列表的序列化器
            serializer_list = BookInfoSerializer(instance=book_list, many=True)
            # 3:转换数据
            # return JsonResponse(serializer_list.data, safe=False)
            return Response(serializer_list.data, status=status.HTTP_200_OK)
    
        def post(self, request):
            """
            新增图书
            路由:POST /books/info/
            """
            #json_bytes = request.body
            #json_str = json_bytes.decode()
            #book_dict = json.loads(json_str, encoding="utf-8")
            # 创建序列化器
            #serializer = BookInfoSerializer(data=book_dict)
            d=request.data
            serializer = BookInfoSerializer(data=d)
            # 校验
            serializer.is_valid(raise_exception=True)
            # 入库
            serializer.save()
            return Response(status=status.HTTP_201_CREATED)
    
    
    class BookAPIView(APIView):
        '''
        查询所有图书,增加图书
        '''
    
        def get(self, request, pk):
            """
            根据图书ID查询图书(页码确定范围)
            路由:GET /books/info/pk/
            """
            try:
                book = BookInfo.objects.get(id=pk)
            except BookInfo.DoesNotExist:
                return Response(status=status.HTTP_404_NOT_FOUND)
            serializer = BookInfoSerializer(instance=book)
            return Response(serializer.data, status=status.HTTP_200_OK)
    
        def put(self, request, pk):
            """
            根据图书ID修改图书
            路由:PUT /books/info/pk/
            """
            try:
                book = BookInfo.objects.get(id=pk)
            except BookInfo.DoesNotExist:
                return Response(status=status.HTTP_404_NOT_FOUND)
            # json_bytes = request.body
            # json_str = json_bytes.decode()
            # book_dict = json.loads(json_str)
            # 2:创建序列化器
            serializer = BookInfoSerializer(instance=book, data=request.data)
            # 3:校验
            serializer.is_valid(raise_exception=True)
            # 4:入库
            serializer.save()
            serializer = BookInfoSerializer(instance=book)
            return Response(serializer.data, status=status.HTTP_200_OK)
    
        def delete(self, request, pk):
            """
            根据图书ID删除图书
            路由:DELETE /books/info/pk/
            """
            try:
                book = BookInfo.objects.get(id=pk)
            except BookInfo.DoesNotExist:
                return Response(status=status.HTTP_404_NOT_FOUND)
            book.delete()
            res = BookInfoSerializer(instance=book).data
            return Response(res, status=status.HTTP_204_NO_CONTENT)
    view5.py

    二、二级视图GenericView

    1、GenericView 与 APIView :

    1. GenericView 继承自APIView,包括(列表视图、详情视图)
    2. 添加了通常的属性
      1. queryset:  查询集:queryset = BookInfo.objects.all()
      2. serializer_class:序列化类:serializer_class = BookInfoModelSerializer
      3. lookup_field:查询字段:默认是pk,可以手动修改成数据库字段
    3. 添加了通常的行为
      1. self.get_queryset()  
      2. self.get_serializer_class()(instance=bookes,many=True)
      3. self.get_serializer(instance=bookes,many=True)
      4.  self.get_object()      #根据lookup_field 到queryset中取出单个对象

    2、通过GenericView完善代码 

    from django.conf.urls import url
    from . import views6 as views
    
    urlpatterns = [
        url(r'^info$', views.BooksAPIView.as_view()),
        url(r'^info/(?P<pk>d+)/$', views.BookAPIView.as_view()),
    ]
    url.py
    from APP01.models import BookInfo, HeroInfo
    from APP01.serializer import BookInfoSerializer, HeroInfoSerializer
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework import status
    from rest_framework.generics import GenericAPIView
    #GenericAPIView 进一步封装,把调用的类封装出来
    
    # 1,列表视图
    class BooksAPIView(GenericAPIView):
        '''
        查询所有屠苏,增加图书
        '''
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
        def get(self, request):
            """
            查询所有图书
            路由:GET /books/info?page=1&page_size=5
            """
            # 2:创建可以序列化列表的序列化器
            serializer_list = self.serializer_class(instance=self.get_queryset(), many=True)
            # 3:转换数据
            # return JsonResponse(serializer_list.data, safe=False)
            return Response(serializer_list.data, status=status.HTTP_200_OK)
    
        def post(self, request):
            """
            新增图书
            路由:POST /books/info/
            """
            # json_bytes = request.body
            # json_str = json_bytes.decode()
            # book_dict = json.loads(json_str, encoding="utf-8")
            # 创建序列化器
            # serializer = BookInfoSerializer(data=book_dict)
            d = request.data
            serializer = self.serializer_class(data=d)
            # 校验
            serializer.is_valid(raise_exception=True)
            # 入库
            serializer.save()
            return Response(status=status.HTTP_201_CREATED)
    
    
    class BookAPIView(GenericAPIView):
        '''
        查询所有图书,增加图书
        '''
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
        def get(self, request, pk):
            """
            根据图书ID查询图书(页码确定范围)
            路由:GET /books/info/pk/
            """
            book = self.get_object()
            serializer = BookInfoSerializer(instance=book)
            return Response(serializer.data, status=status.HTTP_200_OK)
    
        def put(self, request, pk):
            """
            根据图书ID修改图书
            路由:PUT /books/info/pk/
            """
            book = self.get_object()
            # 2:创建序列化器
            serializer = self.serializer_class(instance=book, data=request.data)
            # 3:校验
            serializer.is_valid(raise_exception=True)
            # 4:入库
            serializer.save()
            serializer = self.serializer_class(instance=book)
            return Response(serializer.data, status=status.HTTP_200_OK)
    
        def delete(self, request, pk):
            """
            根据图书ID删除图书
            路由:DELETE /books/info/pk/
            """
            book = self.get_object()
            book.delete()
            res = self.serializer_class(instance=book).data
            return Response(res, status=status.HTTP_204_NO_CONTENT)
    View6.py

    3、Mixin 

    特点:

    1. 提供了基本视图行为(列表视图、详情视图)的操作
    2. 配合二级视图GenericAPIView使用

      功能:

    1.  ListModelMixin    查询所有的数据  List
    2. CreateModelMixin     创建单个对象          create
    3. RetrieveModelMixin  获取单个对象     retrieve
    4. UpdateModelMixin    更新单个对象          update
    5. DestroyModelMixin   删除单个对象          destroy

    2、通过GenericView和Mixin完善代码 

    from APP01.models import BookInfo, HeroInfo
    from APP01.serializer import BookInfoSerializer, HeroInfoSerializer
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from rest_framework import status
    from rest_framework.generics import GenericAPIView
    from rest_framework.mixins import ListModelMixin, CreateModelMixin, DestroyModelMixin, RetrieveModelMixin, 
        UpdateModelMixin
    
    
    # GenericAPIView 进一步封装,把调用的类封装出来
    
    # 1,列表视图
    class BooksAPIView(GenericAPIView, ListModelMixin, CreateModelMixin):
        '''
        查询所有屠苏,增加图书
        '''
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
    
        def get(self, request):
            """
            查询所有图书
            路由:GET /books/info
            """
            return self.list(request)
    
        def post(self, request):
            """
            新增图书
            路由:POST /books/info/
            """
            return self.create(request)
    
    
    class BookAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
        '''
        查询所有图书,增加图书
        '''
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
    
        def get(self, request, pk):
            """
            根据图书ID查询图书(页码确定范围)
            路由:GET /books/info/pk/
            """
            lookup_url_kwarg = "book_id"
            return self.retrieve(request)
    
        def put(self, request, pk):
            """
            根据图书ID修改图书
            路由:PUT /books/info/pk/
            """
            return self.update(request)
    
        def delete(self, request, pk):
            """
            根据图书ID删除图书
            路由:DELETE /books/info/pk/
            """
            return self.destroy()
    View7.py

     三、三级视图

    1、三级视图

      特点:

    1. 如果没有大量自定义的行为,可以使用通用视图(三级视图)解决,
    2. 与二级视图相比,进一步封装了post、get方法

      功能:

    1. ListAPIView             (GenericAPIView,ListModelMixin)             查询所有的数据       post
    2. CreateAPIView        (GenericAPIView,CreateModelMixin)   创建单个对象     get
    3. RetrieveAPIView     (GenericAPIView,RetrieveModelMixin)      获取单个对象     get
    4. UpdateAPIView       (GenericAPIView,UpdateModelMixin)       更新单个对象           put
    5. DestroyAPIView      (GenericAPIView,DestroyModelMixin)       删除单个对象          destroy
    from django.conf.urls import url
    from . import views8 as views
    
    urlpatterns = [
        url(r'^info$', views.BooksAPIView.as_view()),
        url(r'^info/(?P<pk>d+)/$', views.BookAPIView.as_view()),
    ]
    url8.py
    from APP01.models import BookInfo, HeroInfo
    from APP01.serializer import BookInfoSerializer, HeroInfoSerializer
    from rest_framework.views import APIView
    from rest_framework.generics import GenericAPIView,ListAPIView,CreateAPIView,RetrieveAPIView,UpdateAPIView,DestroyAPIView
    
    
    # GenericAPIView 进一步封装,把调用的类封装出来
    
    # 1,列表视图
    class BooksAPIView(ListAPIView,CreateAPIView):
        '''
        查询所有屠苏,增加图书
        '''
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
    
    
    class BookAPIView(RetrieveAPIView,UpdateAPIView,DestroyAPIView):
        '''
        查询所有图书,增加图书
        '''
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
    view8,py

     四、视图集

    1、视图集

      特点:

    1. 可以将一组相关的操作,放在一个类中进行完成
    2. 与三级视图相比,把两个类合并成一个类
    3. 不提供get,post方法,使用retrieve,create方法来替代

    2、视图集之ViewSet

      特点:

    1. 父类:APIView
    2. 作用:不提供任何操作实现
    3. 可以将标准的请求方式(get,post,put,delete)和mixin中的方法做映射
    from django.conf.urls import url
    from . import views9 as views
    
    urlpatterns = [
        url(r'^info/$', views.BookAPIView.as_view({'get':'list'})),
        url(r'^info/(?P<pk>d+)/$', views.BookAPIView.as_view({'get': 'retrieve'})),
    ]
    url9.py
    from APP01.models import BookInfo, HeroInfo
    from APP01.serializer import BookInfoSerializer, HeroInfoSerializer
    from rest_framework import viewsets
    from rest_framework.response import Response
    from django.shortcuts import get_object_or_404
    
    
    # GenericAPIView 进一步封装,把调用的类封装出来
    
    
    class BookAPIView(viewsets.ViewSet):
        '''
        查询所有图书,增加图书
        '''
        def list(self,request):
            queryset = BookInfo.objects.all()
            serializer_class = BookInfoSerializer(queryset,many=True)
            return Response(serializer_class.data)
    
        def retrieve(self,request,pk=None):
            queryset = BookInfo.objects.all()
            book=get_object_or_404(queryset,pk=pk)
            serializer_class = BookInfoSerializer(book)
            return Response(serializer_class.data)
    view9.py

    3、视图集之ReadOnlyModelViewSet

      特点:

    1. 父类:GenericViewSet(三个属性、三个方法)、RetrieveModelMixin,ListModelMixin
    2. 作用:获取单个和所有,拥有三个属性、三个方法
    3. 路由映射

      获取所有和单个对象

    from django.conf.urls import url
    from . import views10 as views
    
    urlpatterns = [
        url(r'^info/$', views.BookAPIView.as_view({'get':'list'})),
        url(r'^info/(?P<pk>d+)/$', views.BookAPIView.as_view({'get': 'retrieve'})),
    ]
    url10.py
    from APP01.models import BookInfo, HeroInfo
    from APP01.serializer import BookInfoSerializer, HeroInfoSerializer
    from rest_framework.viewsets import ReadOnlyModelViewSet
    
    
    
    # GenericAPIView 进一步封装,把调用的类封装出来
    
    
    class BookAPIView(ReadOnlyModelViewSet):
        '''
        查询所有图书,增加图书
        '''
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
    view10.py

    4、视图集之GenericViewSet

      特点:

    1. 父类:GenericAPIView(三个属性、三个方法)ViewSetMixin(将标准请求方式做映射)
    2. 作用:拥有二级视图的三个属性、三个方法
    3. 路由映射

    5、视图集之ModelViewSet(三行代码实现增、删、改、查)

      特点:

    1. 父类:GenericAPIView(三个属性、三个方法)5个mixin类
    2. 作用:所有的增删改查功能,拥有三个属性、三个方法
    3. 路由映射
    from django.conf.urls import url
    from . import views11 as views
    
    urlpatterns = [
        url(r'^info/$', views.BookAPIView.as_view({'get': 'list', 'post': 'create'})),
        url(r'^info/(?P<pk>d+)/$', views.BookAPIView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
    ]
    url11.py
    from APP01.models import BookInfo, HeroInfo
    from APP01.serializer import BookInfoSerializer, HeroInfoSerializer
    from rest_framework.viewsets import ModelViewSet
    
    
    # GenericAPIView 进一步封装,把调用的类封装出来
    
    class BookAPIView(ModelViewSet):
        '''
        查询所有图书,增加图书
        '''
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
    view11.py

    6、视图集之额外动作

      6.1 一个获取评论量大于20的数据接口

    from django.conf.urls import url
    from . import views12 as views
    
    urlpatterns = [
        url(r'^info/$', views.BookAPIView.as_view({'get': 'list', 'post': 'create'})),
        url(r'^info/(?P<pk>d+)/$', views.BookAPIView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
        url(r'^info/bcomment/$', views.BookAPIView.as_view({'get': 'bcomment_book'}))
    ]
    url12.py
    from APP01.models import BookInfo, HeroInfo
    from APP01.serializer import BookInfoSerializer, HeroInfoSerializer
    from rest_framework.viewsets import ModelViewSet
    from rest_framework.response import Response
    
    
    # GenericAPIView 进一步封装,把调用的类封装出来
    
    class BookAPIView(ModelViewSet):
        '''
        查询所有图书,增加图书
        '''
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
    
        def bcomment_book(self, request):
            # 1:获取指定书籍(评论数量大于20)
            books = BookInfo.objects.filter(bread__gt=20)
            # 2:创建序列化器
            serializer = self.get_serializer(books, many=True)
            # 3:返回响应
            return Response(serializer.data)
    view12.py

      6.2 修改书籍编号为1的,评论量修改为99

    from django.conf.urls import url
    from . import views12 as views
    
    urlpatterns = [
        url(r'^info/$', views.BookAPIView.as_view({'get': 'list', 'post': 'create'})),
        url(r'^info/(?P<pk>d+)/$', views.BookAPIView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
        url(r'^info/bcomment/$', views.BookAPIView.as_view({'get': 'bcomment_book'})),
        url(r'^info/bcomment/(?P<pk>d+)/$', views.BookAPIView.as_view({'put': 'update_book_bcomment'})),
    
    ]
    url.py
    from APP01.models import BookInfo, HeroInfo
    from APP01.serializer import BookInfoSerializer, HeroInfoSerializer
    from rest_framework.viewsets import ModelViewSet
    from rest_framework.response import Response
    
    
    # GenericAPIView 进一步封装,把调用的类封装出来
    
    class BookAPIView(ModelViewSet):
        '''
        查询所有图书,增加图书
        '''
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
    
        def bcomment_book(self, request):
            # 1:获取指定书籍(评论数量大于20)
            books = BookInfo.objects.filter(bread__gt=20)
            # 2:创建序列化器
            serializer = self.get_serializer(books, many=True)
            # 3:返回响应
            return Response(serializer.data)
    
        def update_book_bcomment(self, request, pk):
            # 1:获取参数
            book = self.get_object()
            data = request.data
            # 2:创建序列化器
            serializer = self.get_serializer(book, data=data, partial=True)
            # 3:验证,入库
            serializer.is_valid(raise_exception=True)
            serializer.save()
            return Response(serializer.data, status=201)
    view12.py

     五、路由与装饰

       我们发现目前的路由越来越长,可以通过DefaultRouter和SimpleRouter自动生成路由。

      步骤:

    1. 创建路由对象
    2. 注册视图集
    3. 输出结果

      注意点:

    1. 使用DRF可以自动根据前端需要的类型,返回对应格式的数据
    2. 请求的时候在请求头中标记,Accept即可

    1、路由DefaultRouter

    from django.conf.urls import url
    from . import views13 as views
    
    urlpatterns = []
    from rest_framework.routers import SimpleRouter, DefaultRouter
    
    # 1:创建路由对象
    router = DefaultRouter()
    # 2:注册视图集
    router.register('info', views.BookAPIView,)
    urlpatterns += router.urls
    # 3:打印路由
    print(urlpatterns)
    url13.py
    [
     <URLPattern '^info/$' [name='bookinfo-list']>,
     <URLPattern '^info.(?P<format>[a-z0-9]+)/?$' [name='bookinfo-list']>, 
    
     <URLPattern '^info/(?P<pk>[^/.]+)/$' [name='bookinfo-detail']>, 
     <URLPattern '^info/(?P<pk>[^/.]+).(?P<format>[a-z0-9]+)/?$' [name='bookinfo-detail']>,
    
     <URLPattern '^$' [name='api-root']>, 
     <URLPattern '^.(?P<format>[a-z0-9]+)/?$' [name='api-root']>
      ]
    自动生成的路由

    1:获取列表

     

    2:新增

     3:其他功能不再一一测试。

      一共有6个路由(根路径、列表、详情)X2 ,可实现功能的增删改查。

    2、路由SimpleRouter

      simplerouter与defaultrouter相比,生成的路由比较少,有2个路由(列表、详情) ,可实现功能的增删改查。

    from django.conf.urls import url
    from . import views13 as views
    
    urlpatterns = []
    from rest_framework.routers import SimpleRouter, DefaultRouter
    
    # 1:创建路由对象
    router = SimpleRouter()
    # 2:注册视图集
    router.register('info', views.BookAPIView,)
    urlpatterns += router.urls
    # 3:打印路由
    print(urlpatterns)
    url13.py
      [
          <URLPattern '^info/$' [name='bookinfo-list']>, 
          <URLPattern '^info/(?P<pk>[^/.]+)/$' [name='bookinfo-detail']>
      ]
    结果

    3、装饰

      我们发现我们自定义的方法并路由自动生成的时候并没有帮我们生成,这时候我们需要装饰器

    from APP01.models import BookInfo, HeroInfo
    from APP01.serializer import BookInfoSerializer, HeroInfoSerializer
    from rest_framework.viewsets import ModelViewSet
    from rest_framework.response import Response
    from rest_framework.decorators import action
    
    # GenericAPIView 进一步封装,把调用的类封装出来
    
    class BookAPIView(ModelViewSet):
        '''
        查询所有图书,增加图书
        '''
        queryset = BookInfo.objects.all()
        serializer_class = BookInfoSerializer
    
        #生成路由规则:前缀/方法名
        #eg:'^info/bcomment_book/$'
        @action(methods=['GET'],detail=False)
        def bcomment_book(self, request):
            # 1:获取指定书籍(评论数量大于20)
            books = BookInfo.objects.filter(bread__gt=20)
            # 2:创建序列化器
            serializer = self.get_serializer(books, many=True)
            # 3:返回响应
            return Response(serializer.data)
    
        # 生成路由规则:前缀/参数/方法名
        #eg:'^info/(?P<pk>[^/.]+)/update_book_bcomment/$'
        @action(methods=['PUT'], detail=True)
        def update_book_bcomment(self, request, pk):
            # 1:获取参数
            book = self.get_object()
            data = request.data
            # 2:创建序列化器
            serializer = self.get_serializer(book, data=data, partial=True)
            # 3:验证,入库
            serializer.is_valid(raise_exception=True)
            serializer.save()
            return Response(serializer.data, status=201)
    view13.py
     [
          <URLPattern '^info/$' [name='bookinfo-list']>, 
          <URLPattern '^info/bcomment_book/$' [name='bookinfo-bcomment-book']>, 
          <URLPattern '^info/(?P<pk>[^/.]+)/$' [name='bookinfo-detail']>, 
          <URLPattern '^info/(?P<pk>[^/.]+)/update_book_bcomment/$' [name='bookinfo-update-book-bcomment']>
     ]
    结果

    测试一下

     

  • 相关阅读:
    [JSOI2009] 游戏
    CF1148H Holy Diver
    [提高组集训2021] 模拟赛3
    CF1458F Range Diameter Sum
    [游记] CSP2021
    CF1396E Distance Matching
    CF1396D Rainbow Rectangles
    【LeetCode】1. 两数之和
    【随笔】开通博客园过程
    MyISAM与InnoDB的区别是什么?
  • 原文地址:https://www.cnblogs.com/YK2012/p/13253566.html
Copyright © 2020-2023  润新知