DRF04-视图
视图共分三级
他们之间的联系如下
一级是APIView,二级是GenericAPIView,三级则是CreateAPIView等,下面就对这三级视图的使用分别进行讲解
一级视图
APIView之request
-
目的: 知道APIView的特点, 并且可以通过request获取参数
-
特点:
- 1, 继承自View
- 2, 提供了自己的request对象
- get参数: request.query_params
- post参数: request.data
- 3, 提供了自己的response对象
- 4, 并且提供了认证, 权限, 限流等功能
操作流程:
#1,定义类,集成APIView
class BookAPIView(APIView):
def get(self,request):
"""
View获取数据方式:
GET:
request.GET
POST:
request.POST
request.body
APIView获取数据方式
GET:
reqeust.query_params
POST:
request.data
:param request:
:return:
"""
#1,获取APIVIew中的get请求参数
# print(request.query_params)
return http.HttpResponse("get")
def post(self,request):
# 2,获取APIView中的post的参数
print(request.data)
return http.HttpResponse("post")
APIView之Response
-
目的: 可以使用response响应各种数据和状态
-
好处:
- 1,使用一个类, 就可以替代以前View中的各种类型的Response(HttpResponse,JsonResponse….)
- 2, 可以配合状态码status使用
from rest_framework.views import APIView
from django import http
from rest_framework.response import Response
from rest_framework import status
#1,定义类,集成APIView
class BookAPIView(APIView):
def get(self,request):
...
return Response([{"name":"zhangsan"}, {"age":13}],status=status.HTTP_404_NOT_FOUND)
常用的web请求有五大方法,分别是get获取所有,post添加一个,get获取单个,put修改一个,delete删除一个,其中前两个又叫列表视图,后三个叫详情视图。
APIView实现列表视图
可以使用序列化器和APIView对列表视图进行改写
子路由
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^books/$',views.BookListAPIView.as_view())
]
视图类
class BookListAPIView(APIView):
def get(self,request):
#1,查询所有的书籍
books = BookInfo.objects.all()
#2,将对象列表转成字典列表
serializr = BookInfoModelSerializer(instance=books,many=True)
#3,返回响应
return Response(serializr.data)
def post(self,request):
#1,获取参数
data_dict = request.data
#2,创建序列化器
serializer = BookInfoModelSerializer(data=data_dict)
#3,校验,入库
serializer.is_valid(raise_exception=True)
serializer.save()
#4,返回响应
return Response(serializer.data,status=status.HTTP_201_CREATED)
序列化器
from rest_framework import serializers
from booktest.models import BookInfo
#1,定义书籍模型类序列化器
class BookInfoModelSerializer(serializers.ModelSerializer):
class Meta:
model = BookInfo
fields = "__all__"
APIView实现详情视图
可以使用模型类序列化器和APVIew改写详情视图
子路由
from django.conf.urls import url
from . import views
urlpatterns = [
...
url(r'^books/(?P\d+)/$',views.BookDetailAPIView.as_view()),
]
类视图
#3,序列化器和APIView实现详情视图
class BookDetailAPIView(APIView):
def get(self,request,book_id):
#1,获取书籍
book = BookInfo.objects.get(id=book_id)
#2,创建序列化器对象
serializer = BookInfoModelSerializer(instance=book)
#4,返回响应
return Response(serializer.data,status=status.HTTP_200_OK)
def put(self,request,book_id):
#1,获取数据,获取对象
data_dict = request.data
book = BookInfo.objects.get(id=book_id)
#2,创建序列化器对象
serializer = BookInfoModelSerializer(instance=book,data=data_dict)
#3,校验,入库
serializer.is_valid(raise_exception=True)
serializer.save()
#4,返回响应
return Response(serializer.data,status=status.HTTP_201_CREATED)
def delete(self,request,book_id):
#1,删除书籍
BookInfo.objects.get(id=book_id).delete()
#2,返回响应
return Response(status=status.HTTP_204_NO_CONTENT)
二级视图
二级视图,实现列表视图
子路由
url(r'^generic_apiview_books/$',views.BookListGenericAPIView.as_view()),
类视图
#4,二级视图GenericAPIView特点
"""
特点:
1, GenericAPIView,继承自APIView类,为列表视图, 和详情视图,添加了常用的行为和属性。
行为(方法)
get_queryset
get_serializer
属性
queryset
serializer_class
2, 可以和一个或多个mixin类配合使用。
"""
#5,使用二级视图GenericAPIView实现, 列表视图
class BookListGenericAPIView(GenericAPIView):
#1,提供公共的属性
queryset = BookInfo.objects.all()
serializer_class = BookInfoModelSerializer
def get(self,request):
#1,查询所有的书籍
# books = self.queryset
books = self.get_queryset()
#2,将对象列表转成字典列表
# serializr = BookInfoModelSerializer(instance=books,many=True)
# serializr = self.serializer_class(instance=books,many=True)
# serializr = self.get_serializer_class()(instance=books,many=True)
serializr = self.get_serializer(instance=books,many=True)
#3,返回响应
return Response(serializr.data)
def post(self,request):
#1,获取参数
data_dict = request.data
#2,创建序列化器
# serializer = BookInfoModelSerializer(data=data_dict)
serializer = self.get_serializer(data=data_dict)
#3,校验,入库
serializer.is_valid(raise_exception=True)
serializer.save()
#4,返回响应
return Response(serializer.data,status=status.HTTP_201_CREATED)
二级视图,实现详情视图
子路由
url(r'^generic_apiview_books/(?P\d+)/$',views.BookDetailGenericAPIView.as_view()),
类视图
class BookDetailGenericAPIView(GenericAPIView):
#1,提供通用属性
queryset = BookInfo.objects.all()
serializer_class = BookInfoModelSerializer
def get(self,request,pk):
#1,获取书籍
# book = BookInfo.objects.get(id=book_id)
book = self.get_object() #根据book_id到queryset中取出书籍对象
#2,创建序列化器对象
serializer = self.get_serializer(instance=book)
#4,返回响应
return Response(serializer.data,status=status.HTTP_200_OK)
def put(self,request,pk):
#1,获取数据,获取对象
data_dict = request.data
book = self.get_object()
#2,创建序列化器对象
serializer = self.get_serializer(instance=book,data=data_dict)
#3,校验,入库
serializer.is_valid(raise_exception=True)
serializer.save()
#4,返回响应
return Response(serializer.data,status=status.HTTP_201_CREATED)
def delete(self,request,pk):
#1,删除书籍
self.get_object().delete()
#2,返回响应
return Response(status=status.HTTP_204_NO_CONTENT)
get_object方法
理解get_object如何根据pk在queryset获取的单个对象
二级视图GenericAPIView属性方法总结
1, GenericAPIView,继承自APIView类,为列表视图, 和详情视图,添加了常用的行为和属性。
行为(方法)
get_queryset: 获取queryset的数据集
get_serializer: 获取serializer_class序列化器对象
get_object: 根据lookup_field获取单个对象
属性
queryset: 通用的数据集
serializer_class: 通用的序列化器
lookup_field: 默认是pk,可以手动修改id
2, 可以和一个或多个mixin类配合使用。
在上面的案例代码中我们可以看到获取单个对象是如何做的?使用了book = self.get_object()
,很明显这个是个父类方法,因此我们区父类GenericAPIView
中看看,get_object是如何获取单个实例对象的。
get_object()源码解析
以下的代码来自DRF源码,经过我的删减,只保留跟get_object()最相关的东西
- 这里就是我们继承GenericAPIView的时候必须指定的数据集和序列化器
- 这两个字段先记住,后面就用到了
- 如果
lookup_url_kwarg
这个字段为空,就用lookup_field
,第二步中也看到了,lookup_url_kwarg
为空,因此lookup_url_kwarg就等于lookup_field的默认值也就是pk - 断言,如果pk不在self.kwargs中,就会报错,括号里是不满足断言条件时会打印的信息。那self.kwagrs又是哪里来的,点击self.kwagrs跳到了5
- dispatch方法是用来解析请求形式的方法。我们知道,GenericAPIView继承自APIView。在这里,正则解析路由,会将对应的参数放到args或者kwargs,因此7中的pk就放到了self.kwargs中,也就跟前面的lookup_field = 'pk'对应起来了。也就是说get_object()方法默认使用pk主键获取对象。
- 形成过滤条件字典,并且依据此字典从数据集中获取数据对象
- 路由,pk来自这里
- 使用反射获取对应的请求方法,也就是两个get,post,put,delete
- 执行请求方法
MiXin
Mixin可以和二级视图配合使用
先看一下如何使用
路由
url(r'^mixin_generic_apiview_books/$',
views.BookListMixinGenericAPIView.as_view()),
url(r'^mixin_generic_apiview_books/(?P\d+)/$',
views.BookDetailMixinGenericAPIView.as_view()),
类视图
from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
#8,mixin和二级视图GenericAPIView, 实现列表视图, 详情视图
class BookListMixinGenericAPIView(GenericAPIView,ListModelMixin,CreateModelMixin):
#1,提供公共的属性
queryset = BookInfo.objects.all()
serializer_class = BookInfoModelSerializer
def get(self,request):
return self.list(request)
def post(self,request):
return self.create(request)
class BookDetailMixinGenericAPIView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin):
#1,提供通用属性
queryset = BookInfo.objects.all()
serializer_class = BookInfoModelSerializer
# lookup_field = "id"
lookup_url_kwarg = "book_id"
def get(self,request,book_id):
return self.retrieve(request)
def put(self,request,book_id):
return self.update(request)
def delete(self,request,book_id):
return self.destroy(request)
我们自己的视图类必须继承二级视图,mixin类则根据请求选择性的继承。可以看到,使用mixin和二级视图的组合使用,代码更加精简了,我们只需要执行数据集和序列化类就行了。那他是如何做到的?
下面以ListModelMixin为例,查看一下ListModelMixin的源码
class ListModelMixin:
"""
List a queryset.
"""
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
ListModelMixin只有一个list方法,而方法体内部的代码跟我们直接使用二级视图来写的几乎一样。剩下的jigemixin类都是如此,每个mixin类都只有一个方法,方法体的代码就是我们之前自己去写的代码。
mixin类有如下几种
类名称 | 提供方法 | 功能 |
---|---|---|
ListModelMixin | list | 查询所有的数据 |
CreateModelMixin | create | 创建单个对象 |
RetrieveModelMixin | retrieve | 获取单个对象 |
UpdateModelMixin | update | 更新单个对象 |
DestroyModelMixin | destroy | 删除单个对象 |
三级视图
可以看到,二级视图还需要包装增删查改五个方法,自己书写对应的方法来调用父类方法,DRF连这一步都省了,做到了进一步的简练,那就是三级视图
三级视图有如下几种:
类名称 | 父类 | 提供方法 | 功能 |
---|---|---|---|
CreateAPIView | GenericAPIView,CreateModelMixin | post | 创建单个对象 |
ListAPIView | GenericAPIView, ListModelMixin | get | 查询所有的数据 |
RetrieveAPIView | GenericAPIView, RetrieveModelMixin | get | 获取单个对象 |
DestroyAPIView | GenericAPIView, DestroyModelMixin | delete | 删除单个对象 |
UpdateAPIView | GenericAPIView, UpdateModelMixin | put | 更新单个对象 |
三级视图,实现列表,详情视图
子路由
url(r'^third_view/$',views.BookListThirdView.as_view()),
url(r'^third_view/(?P\d+)/$',views.BookDetailThirdView.as_view()),
视图类
from rest_framework.generics import ListAPIView,CreateAPIView
class BookListThirdView(ListAPIView,CreateAPIView):
#1,提供公共的属性
queryset = BookInfo.objects.all()
serializer_class = BookInfoModelSerializer
from rest_framework.generics import RetrieveAPIView,UpdateAPIView,DestroyAPIView
class BookDetailThirdView(RetrieveAPIView,UpdateAPIView,DestroyAPIView):
#1,提供通用属性
queryset = BookInfo.objects.all()
serializer_class = BookInfoModelSerializer
那这么精简内部是如何做的呢?
下面以ListAPIView来举例,其余四个类似
ListAPIView源码
class ListAPIView(mixins.ListModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
可以看到,ListAPIView源码中只有一个方法,并且他帮我们已经继承了二级视图类和对应的mixin类,然后编写了一个函数get调用父类的list方法,这些都是我们以前要做的活儿,现在我们只需要按照要求继承对应的三级视图,给出数据集和序列化器即可,精炼至此,一步留神可能就看不懂了。
视图集
前面的二级视图,三级视图都封装的太好了,尤其是三级视图,方法名都给我们想好了,对于我们而言自由度低了点,视图集可以帮我们映射请求与方法
下面就几个常用的视图集来进行讲解
ViewSet
一个最简单的视图集,提供请求映射的功能
子路由
url(r'^viewset/$',views.BooksViewSet.as_view({'get': 'list'})),
url(r'^viewset/(?P\d+)/$',views.BooksViewSet.as_view({'get': 'retrieve'}))
类视图
from django.shortcuts import get_object_or_404
from booktest.serializer import BookInfoModelSerializer
from rest_framework import viewsets
class BooksViewSet(viewsets.ViewSet):
"""
A simple ViewSet for listing or retrieving books.
"""
def list(self, request):
queryset = BookInfo.objects.all()
serializer = BookInfoModelSerializer(instance=queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
queryset = BookInfo.objects.all()
book = get_object_or_404(queryset, pk=pk)
serializer = BookInfoModelSerializer(instance=book)
return Response(serializer.data)
看一下,ViewSet做了什么
ViewSet源码
class ViewSet(ViewSetMixin, views.APIView):
"""
The base ViewSet class does not provide any actions by default.
"""
pass
ViewSet只做了个继承操作,APIView已经学过了,那么as_view这个方法定然来自ViewSetMixin,且看一下as_view做了什么?不过在此之前我们需要再看一段代码,这段代码有助于我们了解as_view是怎么做的
这段代码中,as_view是ViewSetMixin中定义的方法,Tmp继承了as_view,使用Tmp调用as_view,as_view中的cls指代的就是Tmp,这个和self是一个意思,他们指代的都是最外层调用的那个类或者对象,而不是中间继承的某个类或者对象。
ViewSetMixin修剪过的代码
这整的映射就是这么形成的。
当我们的请求触发请求所有的数据的时候,走的是第一个路由,第一个路由执行的是BooksViewSet.as_view({'get': 'list'})
当我们的请求触发请求单个数据对象的时候,走的是第二个路由,第二个路由执行的是BooksViewSet.as_view({'get': 'retrieve'})
因此虽然两个都是get请求,但因为触发的路由不一样,执行的as_view也不一样,实际执行的函数不一样,因此不会行成冲突。而在以前,列表请求和详情请求我们需要定义两个视图类,现在可以写在一个视图类里面,函数名字我们也可以随便取,在路由中制定好请求函数映射关系就好了。
ReadOnlyModelViewSet
可以使用ReadOnlyModelViewSet获取所有, 和单个数据
子路由
url(r'^readonly_viewset/$', views.BooksReadOnlyModelViewSet.as_view({'get': 'list'})),
url(r'^readonly_viewset/(?P\d+)/$', views.BooksReadOnlyModelViewSet.as_view({'get': 'retrieve'})),
类视图
from rest_framework.viewsets import ReadOnlyModelViewSet
class BooksReadOnlyModelViewSet(ReadOnlyModelViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoModelSerializer
ReadOnlyModelViewSet源码探寻
也就是说ReadOnlyModelViewSet其实就是三级视图加基础视图集ViewSetMixin的合并,不过如此。
ModelViewSet
使用ModelViewSet实现列表视图+详情视图功能
子路由
url(r'^model_viewset/$',
views.BookModelViewSet.as_view({'get': 'list',"post":"create"})),
url(r'^model_viewset/(?P\d+)/$',
views.BookModelViewSet.as_view({'get': 'retrieve','put':'update','delete':'destroy'})),
类视图
from rest_framework.viewsets import ModelViewSet
class BookModelViewSet(ModelViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoModelSerializer
ModelViewSet源码探寻
可以看到ModelViewSet只是五个三级视图和基础视图集ViewSetMixin的结合
至此,DRF的视图算是告一段落了。