斑马斑马-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']>
 ]
结果

测试一下

 

 

 

posted @ 2020-07-07 16:07  逍遥小天狼  阅读(375)  评论(0编辑  收藏  举报