在 RESTful 架构中,对资源的常规操作无非就是查询、新增、修改、删除等这么几种。为此,django-rest-framework 分别提供了对应通用类视图函数。但是,如果对同一个资源的不同操作逻辑分散在各个视图函数中,从逻辑上来说不太合理,实际中管理起来也不是很方便,还会产生很多重复性的代码。因此,django-rest-framework 引入了视图集(Viewsets),把对同一个资源的不同操作,集中到一个类中。同样的,针对 Web 开发中的常见逻辑,django-rest-framework 也提供了通用视图集,进一步简化开发工作。
使用视图集的一个更大的好处,就是可以配合 django-rest-framework 提供的路由器(router),自动生成 API 的 URL,不需要我们再手工将 URL 模式和视图函数绑定了。所以大部分情况下,即使对资源只有一种操作,我们一般也会使用视图集。
先来看看博客首页文章列表视图集的代码:
blog/views.py
from rest_framework import viewsets
from rest_framework import mixins
class PostViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
serializer_class = PostListSerializer
queryset = Post.objects.all()
pagination_class = PageNumberPagination
permission_classes = [AllowAny]
所有视图集都要继承视图集的基类。视图集也有 2 个基类:ViewSet
和 GenericViewSet
,前者是最基本的视图集类,后者拓展自前者,拓展了很多 Web 开发中的通用逻辑。
要注意一点的是,视图集基类提供的是除资源操作以外的通用逻辑(例如 HTTP 请求预处理、HTTP 响应后处理、认证、鉴权等),而对于资源的操作(如序列化、更新、删除资源等)则放在相应的 Mixin 混入类里。django-rest-framework 提供了资源操作的 5 个混入类,分别对应资源的创建、查询、更新、删除。
-
CreateModelMixin
提供
create
方法用于创建资源 -
ListModelMixin 和 RetrieveModelMixin
提供 list 和 retrieve,分别用于获取资源列表和单个资源
-
UpdateModelMixin
提供 update 方法用于更新资源
-
DestroyModelMixin
提供 destroy 方法用于删除资源
此外,create、list、retrieve、update、destroy 的方法名会被映射为对应的 action,称为对资源操作的一个动作。前面说到视图集的一个最大好处就是可以使用路由器(router)自动生成 URL 模式。URL 正是根据 action 的类型来生成的,后面我们会具体说到。
好了,视图集已经创建完毕,接下来我们从视图集生成视图函数,并绑定 URL。
blog/views.py
index = PostViewSet.as_view({'get': 'list'})
blog/urls.py
app_name = "blog"
urlpatterns = [
# ...
# path("api/index/", views.IndexPostListAPIView.as_view()),
path("api/index/", index),
]
等等,不是说视图集的一个好处是使用路由器自动生成 URL 模式吗?为什么还要手工创建视图函数,然后绑定 URL?
别急,这里只是演示一下如何从视图集生成视图函数并绑定 URL,这样能够帮助你更好地理解视图集的工作方式。事实上,使用路由器自动生成 URL 模式时,路由器内部就是采用了和上面手工生成视图函数并绑定 URL 一样的方式。
路由器的使用非常简单,我们在 初始化 RESTful API 风格的博客系统 中引入了 DefaultRouter 以开启 API 交互后台,DefaultRouter 实例化时默认帮我们注册了一个 API 交互后台的根视图,现在要注册一个新的视图,调用其 register
方法就可以了:
blogproject/urls.py
from blog.views import PostViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'posts', PostViewSet, basename='post')
Django-rest-framework 提供 SimpleRouter
和 DefaultRouter
两个路由器类,后者是对前者的拓展,因此通常情况下都使用后者。DefaultRouter 增加了一个 api 的根路由,访问根路由的 URL 就可以看到其他注册的全部 api 路由,一会儿我们将会看到具体的效果。
视图集自动生成 URL 模式非常简单,只需实例化一个路由器,然后调用其 register
方法,这个方法接收 3 个参数,第一个参数是 URL 前缀,所有从注册的视图集生成的 URL 都会带有这个前缀。第二个参数就是视图集,第三个参数 basename 用于指定视图集生成的视图函数名的前缀。在 django 的 URL 中,一条路由通常由 URL 模式,对应的视图函数和视图函数名组成。视图函数名的作用主要用于解析视图函数所对应的 URL。视图集最终会被转为多个视图函数,那么这个视图函数的名字是什么呢?django-rest-framework 的默认生成规则是 basename-action。
例如这里 basename='post'
,列出资源列表的 action 为 list(见上一篇教程中关于 action 的讲解),所以生成的获取文章资源列表的视图函数名为 post-list,使用 reverse('post-list') 就可以解析出获取文章资源列表的 API(URL)。
basename 可以不指定,django-rest-framework 会自动从视图集 get_queryset 方法返回的结果所关联的 model 获取一个默认值,其值为 model 名小写。不过,根据 Python 之禅,显式优于隐式,因此即使你设置的 basename 和 django-rest-framework 默认生成的一样,也比不指定要好。
刚才说了,我们使用 DefaultRouter 这个路由器,它会自动帮我们注册一个根路由,来看看根路由下有什么。
运行开发服务器,访问 http://127.0.0.1:8000/api/,界面如下:
django-rest-framework 为我们自动生成了 API 交互后台,在这个界面中可以和我们创建的 API 交互,非常方便。API 交互后台首页是所有注册的视图集对应的 URL。目前只有一条 /api/posts/,点击超链接进去,可以看到 /api/posts/ 的返回结果,即全部文章列表。
但是,目前我们的 api 一股脑将全部文章列表的返回了。但是我们的博客文章列表是有分页功能的,接下来我们就使用 django-rest-framework 提供的分页辅助类,一行代码就可以完成分页功能。
关注公众号加入交流群