文章目录
一、Generics:工具视图
generics 是工具视图,可以实现极简化接口编写操作。
工具视图都是 GenericAPIView 的子类,不同的子类继承不同工具类,重写请求方法。
1.群查与单增:ListCreateAPIView
查看源码
# 继承了视图基类 GenericAPIView,工具类 ListModelMixin,CreateModelMixin实现群查和单增
class ListCreateAPIView(mixins.ListModelMixin,
mixins.CreateModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset or creating a model instance.
"""
# 群查
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
# 单增
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
代码实现
urls.py
from django.conf.urls import url, include
from api import views
urlpatterns = [
url(r'^v4/books/$', views.BookListCreateView.as_view()),
url(r'^v4/books/(?P<pk>.*)/$', views.BookListCreateView.as_view()),
]
工具视图的功能如果满足需求,只需要继承工具视图,添加 queryset,serializer_class
views.py
class BookListCreateView(ListCreateAPIView):
queryset = models.Book.objects.filter(is_delete=False)
serializer_class = serializers.BookModelSerializer
测试接口
群查
单增
入库成功
其他方法
根据上述源码分析与示例和下面的图示,我们可以很容易知道这些类的功能和用法
2.添加其他接口
代码实现
# 需要什么接口,直接继承就行
# 比如我们在群查,单增的基础上,添加单改接口
class BookListCreateView(ListCreateAPIView, UpdateAPIView):
queryset = models.Book.objects.filter(is_delete=False)
serializer_class = serializers.BookModelSerializer
接口测试
修改成功
3.后续问题
但是上述虽然可以实现简单接口,但是有时候需求会很复杂。
比如,来自前端用户的数据格式并不是和我们规定的一样,有可能传来空值,错误字符等等。这就需要对 request.data 进行过滤,尤其是在入库的时候。
另外,如果数据有误,DRF 并不知道你的字段是哪出的问题,所以抛异常是只会是数据错误。我们需要对每个字段的每种错误类型给出对应的返回值。
可以在 UpdateModelMixin 源码中看到,request.data 并没有进行过滤。
class UpdateModelMixin:
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
# request.data 并没有进行过滤
serializer = self.get_serializer(instance, data=request.data, partial=partial)
# ...
二、Viewsets:视图集
1.简单使用
DRF 提供了 Viewsets.py 视图集,再次封装之前的操作。最主要的是,可以通过设置 请求-函数
映射关系,来将请求方式与原有方法或自定义方法对应执行。
查看源码
发现没有提供实际的方法
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
"""
The GenericViewSet class does not provide any actions by default,
but does include the base set of generic view behavior, such as
the `get_object` and `get_queryset` methods.
"""
pass
但是看到它继承了 ViewSetMixin,GenericAPIView
查看 ViewSetMixin 类的 as_view 方法
GenericViewSet 和 ViewSet 都继承了 ViewSetMixin,as_view 可以配置 请求-函数
映射
比如view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
class ViewSetMixin:
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
#...
def view(request, *args, **kwargs):
# 这里 cls 去解析前边例子中的 {'get': 'list', 'post': 'create'}
self = cls(**initkwargs)
self.action_map = actions
# methods拿到请求方法,比如get
for method, action in actions.items():
handler = getattr(self, action)
# 映射method get到执行函数handler list
setattr(self, method, handler)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
# 继承 APIView 中的 dispatch 进行分发
return self.dispatch(request, *args, **kwargs)
# ...
代码实现
这样的好处是,各种需求的接口的请求方式都可以用不同函数定义返回值。比如十大接口对应十个函数,分别码代码。
views.py
class BookGenericViewSet(RetrieveModelMixin, ListModelMixin, GenericViewSet):
queryset = models.Book.objects.filter(is_delete=False)
serializer_class = serializers.BookModelSerializer
def get_list(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def get_obj(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
urls.py
from django.contrib import admin
from django.urls import path
from django.conf.urls import url, include
from django.views.static import serve
from django.conf import settings
from api import views
urlpatterns = [
# ...
url(r'^v5/books/$', views.BookGenericViewSet.as_view({'get':'get_list'})),
url(r'^v5/books/(?P<pk>.*)/$', views.BookGenericViewSet.as_view({'get':'get_obj'})),
]
2.ModelViewSet:最全的封装类
查看源码
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
A viewset that provides default `create()`, `retrieve()`, `update()`,
`partial_update()`, `destroy()` and `list()` actions.
"""
pass
代码实现
views.py
# 继承 ModelViewset 会直接拥有六大接口:单查,群查,单增,单删,单整体改,单局部改
# 需要注意:Destroy 需要重写
class BookModelViewSet(ModelViewSet):
queryset = models.Book.objects.filter(is_delete=False)
serializer_class = serializers.BookModelSerializer
# 删除操作
def destroy(self, request, *args, **kwargs):
instance = self.get_object() # type: models.Book
if not instance:
return APIResponse(1, "Delete fail")
instance.is_delete = True
instance.save()
return APIResponse(1, "Delete successful")
urls.py
from django.conf.urls import url
from api import views
urlpatterns = [
url(r'^v6/books/$', views.BookModelViewSet.as_view({'get':'list', 'post':'create'})),
url(r'^v6/books/(?P<pk>.*)/$', views.BookModelViewSet.as_view({'get': 'retrieve', 'post':'create', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})),
]
接口测试
群查
单查
单删
查看数据库
单增
入库成功
等等
总结
GenericAPIView 与 APIView 的区别与适用场景
1. GenericAPIView 视图类
GenericView 继承 GenericAPIView 视图类
适用于标准的接口请求,或实现标准的 Model 类操作接口。
案例: 用户查询时,发送 GET 请求,返回数据。
2. APIView 视图类
ViewSet 继承 APIView 视图类
实现不需要 Model 类操作,或非标准的 Model 类操作接口。比如,POST请求在标准的 Model 类操作用于新增接口,但以下案例并不符合这个标准。
案例 1: 请求手机验证码时,发送 POST 请求,不需要 Model 类的参与。
案例 2: 用户登录时,发送的 POST 请求,并不是完成数据的新增。POST 只是用于提交数据,返回值也不是登录用户信息,而是登录的认证信息。