Django DRF ViewSet(十)

一、Request和Response

  1. Request

REST framework 传入视图的request对象不再是Django默认的HttpRequest对象,而是REST framework提供的扩展了HttpRequest类的**Request**类的对象

REST framework 提供了**Parser**解析器,在接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON、表单等)将请求数据进行parse解析,解析为类字典对象保存到**Request**对象中。

#DRF中的请求导入方式
from rest_framework.request import Request
Request.data #请求数据
Request.query_params====>Django.GET

常用属性

1).data

`request.data` 返回解析之后的请求体数据。类似于Django中标准的`request.POST`和 `request.FILES`属性,但提供如下特性:

- 包含了解析之后的文件和非文件数据

- 包含了对POST、PUT、PATCH请求方式解析后的数据

- 利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据

2).query_params

`request.query_params`与Django标准的`request.GET`相同,只是更换了更正确的名称而已。

2. Response

REST framework提供了一个响应类`Response`,使用该类构造响应对象时,响应的具体数据内容会被转换(render渲染)成符合前端需求的类型。

REST framework提供了`Renderer` 渲染器,用来根据请求头中的`Accept`(接收数据类型声明)来自动转换响应数据到对应格式。如果前端请求中未进行Accept声明,则会采用默认方式处理响应数据,我们可以通过配置来修改默认响应格式。

#DRF中的响应导入方式
from rest_framework.response import Response
Response(data=Response.data,status=200,template_name=None,headers=None,content_type=None)

`data`数据不要是render处理之后的数据,只需传递python的内建类型数据即可,REST framework会使用`renderer`渲染器处理`data`。

`data`不能是复杂结构的数据,如Django的模型类对象,对于这样的数据我们可以使用`Serializer`序列化器序列化处理后(转为了Python字典类型)再传递给`data`参数。

参数说明:

- `data`: 为响应准备的序列化处理后的数据;

- `status`: 状态码,默认200;

- `template_name`: 模板名称,如果使用`HTMLRenderer` 时需指明;

- `headers`: 用于存放响应头信息的字典;

- `content_type`: 响应数据的Content-Type,通常此参数无需传递,REST framework会根据前端所需类型数据来设置该参数。

常用属性:

1).data传给response对象的序列化后,但尚未render处理的数据

2).status_code状态码的数字

3).content经过render处理后的响应数据

Django DRF ViewSet(十)_第1张图片

  • 视图

 2.1 两个基类

1) APIView 

from rest_framework.views import APIView

 APIView是REST framework提供的所有视图的基类,继承自Django的View父类。

Django DRF ViewSet(十)_第2张图片

APIView与View的不同之处在于:

(1)传入到视图方法中的是REST framework的`Request`对象,而不是Django的`HttpRequeset`对象;

(2)视图方法可以返回REST framework的`Response`对象,视图会为响应数据设置(render)符合前端要求的格式;

(3)任何`APIException`异常都会被捕获到,并且处理成合适的响应信息;

(4)在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。

支持定义的属性:

authentication_classes 列表或元祖,身份认证类

permissoin_classes 列表或元祖,权限检查类

throttle_classes 列表或元祖,流量控制类

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

使用APIView实现增删改查所有图书:

views.py文件

################1.APIView视图###############################
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
# from django.views import View
# 使用APIView实现增删改查所有图书:
#继承APIView
class BookListAPIView(APIView):
    """列表视图"""
    def get(self,request):
        #1.查询所有的图书
        all=BookInfo.objects.all()
        # 2.序列化数据,把模型对象转换成字典
        # instance 默认参数
        # many=True 显示多个数据
        #构造序列对象
        ser=BookInfoSerializer(instance=all,many=True)
        # print(ser.data)
        # response =Response(ser.data)
        # print(response.data) #响应对象未渲染处理的数据
        # print(response.content) # 处理后要响应给前端的数据
        # return response
        #响应
        return  Response(ser.data)

        pass

    def post(self,request):
        '''添加数据'''
        #获取前端传入请求参数
        data=request.data
        #使用序列化器进行反序列哈
        ser=BookInfoSerializer(data=data)
        #校验 raise_exception显示错误消息
        ser.is_valid(raise_exception=True)
        #保存
        ser.save()
        #响应
        return Response(ser.data,status=status.HTTP_201_CREATED)
        pass
#继承APIView
class BookDataAPIView(APIView):
    '''详情视图 '''

    def get(self, request,pk):
        '''查询指定的模型对象=>必须指定id'''
        try:
            book=BookInfo.objects.get(id=pk)
        except  BookInfo.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)
        # 构造序列化器对象
        ser =BookInfoSerializer(instance=book)
        # 响应
        return Response(ser.data)
        pass


    def put(self,request,pk):
        '''更新指定的模型对象=>必须指定id,需要校验'''
        try:
            book=BookInfo.objects.get(id=pk)
        except BookInfo.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)
        # 构造序列化器对象,获取前端传入的请求体数据
        ser = BookInfoSerializer(instance=book,data=request.data)
        #校验
        ser.is_valid(raise_exception=True)
        #保存
        ser.save()
        #响应
        return Response(ser.data)
        pass


    def delete(self,request,pk):
        '''删除指定的模型对象=>必须指定id'''
        '''查询指定的模型对象=>必须指定id'''
        try:
            book = BookInfo.objects.get(id=pk)
        except  BookInfo.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)
        #删除
        book.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

 urls.py文件

 #APIView视图
    path('books/',views.BookListAPIView.as_view()),  #查询所有的图书数据, 类视图;注意:as_view()
    re_path(r'^books/(?P\d+)/$',views.BookDataAPIView.as_view()) #查询详情数据, 类视图;注意:as_view() 必须有()
]

 效果:

Django DRF ViewSet(十)_第3张图片

Django DRF ViewSet(十)_第4张图片

Django DRF ViewSet(十)_第5张图片

Django DRF ViewSet(十)_第6张图片  

Django DRF ViewSet(十)_第7张图片

Django DRF ViewSet(十)_第8张图片

 删除之后,再查看:

Django DRF ViewSet(十)_第9张图片

2) GenericAPIView

from rest_framework.generics import GenericAPIView

继承自APIVIew,增加了对于列表视图和详情视图可能用到的通用支持方法。通常使用时,可搭配一个或多个Mixin扩展类。

源码如下:

 Django DRF ViewSet(十)_第10张图片

支持定义的属性:

(1) 列表视图与详情视图通用:

  queryset 列表视图的查询集

  serializer_class 视图使用的序列化器

(2) 列表视图使用:

  pagination_class 分页控制类

  filter_backends 过滤控制后端

(3) 详情页视图使用:

  lookup_field 查询单一数据库对象时使用的条件字段,默认为'`pk`'

  lookup_url_kwarg 查询单一数据时URL中的参数关键字名称,默认与look_field相同

常用方法:

  1. get_queryset(self)  返回视图使用的查询集,是列表视图与详情视图获取数据的基础,默认返回`queryset`属性,可以重写
  2. get_serializer(self, *args, **kwargs) 返回序列化器对象,被其他视图或扩展类使用,如果我们在视图中想要获取序列化器对象,可以直接调用此方法。
  3. get_object(self) 返回详情视图所需的模型类数据对象,默认使用`lookup_field`参数来过滤queryset。 在试图中可以调用该方法获取详情信息的模型类对象。

使用GenericAPIView实现增删改查所有图书:

views.py文件

################2.GenericAPIView视图###############################
from rest_framework.generics import GenericAPIView
class BookGenericAPIView(GenericAPIView):
    '''列表查询'''
    # 指定查询集'数据来源'
    queryset =BookInfo.objects.all()
    # 指定序列化器类
    serializer_class =BookInfoSerializer

    #查询方法
    def get(self,request):
        # 获取数据集
        find=self.get_queryset()
        # 返回序列化器对象
        ser=self.get_serializer(find,many=True)
        return Response(ser.data)


    def post(self, request):
        '''添加数据'''
        # 获取前端传入请求参数
        data = request.data
        # 使用序列化器进行反序列哈
        ser = BookInfoSerializer(data=data)
        # 校验 raise_exception显示错误消息
        ser.is_valid(raise_exception=True)
        # 保存
        ser.save()
        # 响应
        return Response(ser.data, status=status.HTTP_201_CREATED)


class BookDataGenericAPIView(GenericAPIView):
    '''详情视图'''
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer

    def get(self,request,pk):
        '''根据id查询数据'''
        book=self.get_object()
        ser=self.get_serializer(book)
        return Response(ser.data)
        pass

    def put(self, request, pk):
        '''根据id更新数据'''
        book = self.get_object()
        #请求数据
        ser = self.get_serializer(book,request.data)
        #校验
        ser.is_valid(raise_exception=True)
        #保存
        ser.save()
        return Response(ser.data)
        pass

    def delete(self, request, pk):
        '''根据id删除数据,有可能发生异常'''
        try:
          book = self.get_object()
        except BookInfo.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)
        # 删除模型对象
        book.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
        pass

urls.py文件

  # GenericAPIView视图
    path('booksg/', views.BookGenericAPIView.as_view()),  # 查询所有的图书数据, 类视图;注意:as_view()
    re_path(r'^booksg/(?P\d+)/$', views.BookDataGenericAPIView.as_view()),  # 查询详情数据, 类视图;注意:as_view() 必
]

 Django DRF ViewSet(十)_第11张图片

Django DRF ViewSet(十)_第12张图片 Django DRF ViewSet(十)_第13张图片

Django DRF ViewSet(十)_第14张图片

Django DRF ViewSet(十)_第15张图片

 Django DRF ViewSet(十)_第16张图片

2.2五个扩展类

  1. ListModelMixin :列表视图

源码:

Django DRF ViewSet(十)_第17张图片

 views.py

from rest_framework.mixins import ListModelMixin
from rest_framework.generics import  GenericAPIView  #指定过滤化器queryset
class BookListModelMixin(ListModelMixin,GenericAPIView):
    '''列表视图'''
    #指定查询集 -数据来源
    queryset=BookInfo.objects.all()
    #指定序列化器
    serializer_class = BookInfoSerializer

    def get(self,request):
        return self.list(request)


urls.py


 # ListModelMixin
path('books_lmm/',views.BookListModelMixin.as_view())

 Django DRF ViewSet(十)_第18张图片

2.CreateModelMixin:创建资源的视图

源码:

 Django DRF ViewSet(十)_第19张图片

 views.py

from rest_framework.mixins import CreateModelMixin
class BookCreateModelMixinView(CreateModelMixin,ListModelMixin,GenericAPIView):
    '''创建视图'''
    #指定查询集 -数据来源
    queryset=BookInfo.objects.all()
    #指定序列化器
    serializer_class = BookInfoSerializer
    #查询
    def get(self,request):
        return self.list(request)
    #添加
    def post(self,request):
        return self.create(request)

urls.py
#CreateModelMixin
path('books_cmm/',views.BookCreateModelMixinView.as_view()),

发生异常;

RuntimeError: You called this URL via POST, but the URL doesn't end in a slash and you have APPEND_SLASH set. Django can't redirect to the slash URL while maintaining POST data. Change your form to point to 127.0.0.1:8000/books_cmm/ (note the trailing slash), or set APPEND_SLASH=False in your Django settings.

解决:

访问时不应该访问ip/books,

而应该访问ip/books/ 少了一个斜杠

Django DRF ViewSet(十)_第20张图片

3. RetrieveModelMixin 详情视图

源码:

Django DRF ViewSet(十)_第21张图片

 views.py

from rest_framework.mixins import RetrieveModelMixin
class BookRetrieveModelMixinView(RetrieveModelMixin,GenericAPIView):
    '''详情视图'''
    #指定查询集 -数据来源
    queryset=BookInfo.objects.all()
    #指定序列化器
    serializer_class = BookInfoSerializer
    #查询
    def get(self,request,pk):
        return self.retrieve(request)

urls.py
# RetrieveModelMixin
re_path(r'^books_xmm/(?P\d+)/$', views.BookRetrieveModelMixinView.as_view()),   # 查询详情数据, 类视图;注意:as_view() 必

Django DRF ViewSet(十)_第22张图片

4 UpdateModelMixin: 更新视图

源码:

 Django DRF ViewSet(十)_第23张图片

 views.py

from rest_framework.mixins import UpdateModelMixin
class BookUpdateModelMixinView(UpdateModelMixin,RetrieveModelMixin, GenericAPIView):
    '''更新视图'''
    # 指定查询集 -数据来源
    queryset = BookInfo.objects.all()
    # 指定序列化器
    serializer_class = BookInfoSerializer

    # 查询一个
    def get(self, request,pk):
        return self.retrieve(request,pk)
     # 更新
    def put(self, request,pk):
        return self.update(request,pk)


urls.py
#UpdateModelMixin
re_path(r'^books_umm/(?P\d+)/$',views.BookUpdateModelMixinView.as_view()),

 Django DRF ViewSet(十)_第24张图片

 Django DRF ViewSet(十)_第25张图片

5 DestroyModelMixin:删除视图

源码:

Django DRF ViewSet(十)_第26张图片

views.py 

from rest_framework.mixins import DestroyModelMixin
class BookDestroyModelMixinView(DestroyModelMixin,RetrieveModelMixin,UpdateModelMixin, GenericAPIView):
    '''删除视图'''
    # 指定查询集 -数据来源
    queryset = BookInfo.objects.all()
    # 指定序列化器
    serializer_class = BookInfoSerializer

    # 查询一个
    def get(self, request,pk):
        return self.retrieve(request,pk)
     # 更新
    def put(self, request,pk):
        return self.update(request,pk)
     # 删除
    def delete(self, request,pk):
        return self.destroy(request,pk)

Urls.py
# DestroyModelMixin
re_path(r'^books_umm/(?P\d+)/$',views.BookDestroyModelMixinView.as_view()),

 Django DRF ViewSet(十)_第27张图片

2.3 子类视图

2.4视图集ViewSet

使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中:

- GET: list() 提供一组数据

- GET: retrieve() 提供单个数据

- POST :create() 创建数据

- PUT :update() 更新数据

- PATCH: partail_update, 更新部分数据

- DELETE:destory() 删除数据

ViewSet视图集类不再实现get()、post()等方法,而是实现动作 action 如 list() 、create() 等。继承自APIView,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。

视图集只在使用as_view()方法的时候,才会将action动作与具体请求方式对应上

from rest_framework.viewsets import ViewSet
class BookViewSet(ViewSet):
    '''视图集'''
    def list(self,request):
        '''展示列表数据'''
        #获取数据
        all=BookInfo.objects.all()
        #序列化
        ser=BookInfoSerializer(all,many=True)
        #返回数据
        return Response(ser.data)

    def retrieve(self,request,pk):
        '''获取单个数据'''
        try:
            book=BookInfo.objects.get(id=pk)
        except BookInfo.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)
        ser=BookInfoSerializer(book)
        return Response(ser.data)


#视图集 ViewSet
#as_view中传入字典参数: 第一个参数:get,post,put,delete等等; 第二个参数:关联的方法名
#as_view中不能定义相同的请求
path('booksv/', views.BookViewSet.as_view({"get": "list"})),
re_path(r'^booksv/(?P\d+)/$',views.BookViewSet.as_view({"get":"retrieve"})),

 Django DRF ViewSet(十)_第28张图片

Django DRF ViewSet(十)_第29张图片

2.4.1 ModelViewSet

 继承自GenericAPIVIew,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。

 Django DRF ViewSet(十)_第30张图片

##########################视图集################
from rest_framework.viewsets import ViewSet
class BookViewSet(ViewSet):
    '''视图集'''
    def list(self,request):
        '''展示列表数据'''
        #获取数据
        all=BookInfo.objects.all()
        #序列化
        ser=BookInfoSerializer(all,many=True)
        #返回数据
        return Response(ser.data)

    def retrieve(self,request,pk):
        '''获取单个数据'''
        try:
            book=BookInfo.objects.get(id=pk)
        except BookInfo.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)
        ser=BookInfoSerializer(book)
        return Response(ser.data)


#视图集 ViewSet
#as_view中传入字典参数: 第一个参数:get,post,put,delete等等; 第二个参数:关联的方法名
#as_view中不能定义相同的请求
path('booksv/', views.BookViewSet.as_view({"get": "list"})),
re_path(r'^booksv/(?P\d+)/$',views.BookViewSet.as_view({"get":"retrieve"})),

Django DRF ViewSet(十)_第31张图片

Django DRF ViewSet(十)_第32张图片

2.4.2视图集中定义附加action动作

在视图集中,除了上述默认的方法动作外,还可以添加自定义动作。

添加自定义动作需要使用`rest_framework.decorators.action`装饰器。

以action装饰器装饰的方法名会作为action动作名,与list、retrieve等同。

action装饰器可以接收两个参数:

  1. methods: 该action支持的请求方式,列表传递
  2. detail: 表示是action中要处理的是否是视图资源的对象(即是否通过url路径获取主键)

     True 表示使用通过URL获取的主键对应的数据对象

     False 表示不使用URL获取主键

##########################ModelViewSet################
from rest_framework.decorators import action
from rest_framework.viewsets import ModelViewSet
class BookViewSetAction(ModelViewSet):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer

    @action(methods=['get'],detail=False)
    def later(self,request):
        '''最新的图书信息'''
        # 获取最后一本书
        book =BookInfo.objects.latest('id')
        serializer=self.get_serializer(book)
        return Response(serializer.data)

    # detail为True,表示要处理具体与pk主键对应的BookInfo对象
    # books/pk/read/
    @action(methods=['put'],detail=True)
    def readCount(self,request,pk):
        """修改图书的阅读量数据"""
        book =self.get_object()
        book.read_count=request.data.get('read_count')
        book.save()
        serializer=self.get_serializer(book)
        return Response(serializer.data)

#ModelViewSet 注意:遵循ViewSet的规则,必须传字典参数哦!!否则发生错误!!
# ypeError: The `actions` argument must be provided when calling `.as_view()` on a ViewSet. For example `.as_view({'get': 'list'})`
#视图集中包含附加action的
path('books_m/', views.BookViewSetAction.as_view({'get': 'later'})),
re_path(r'^booksm/(?P\d+)/read/$', views.BookViewSetAction.as_view({'put':'readCount'})),

Django DRF ViewSet(十)_第33张图片 http://127.0.0.1:8000/booksm/13/read/

Django DRF ViewSet(十)_第34张图片

2.5. DefaultRouter与SimpleRouter的区别

DefaultRouter与SimpleRouter的区别是,DefaultRouter会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据。`DefaultRouter继承自SimpleRouter`

 Django DRF ViewSet(十)_第35张图片

Django DRF ViewSet(十)_第36张图片

 使用SimpleRouter则会报错

Django DRF ViewSet(十)_第37张图片

Django DRF ViewSet(十)_第38张图片

你可能感兴趣的:(Django)