泽恩小站-教程区 Help

视图集

通过前几章的折腾,我们已经把文章增删改查都完成了。经过合理运用类和继承的理念,代码已经相当精简了。但是, DRF 框架提供了更高层的抽象,可以让代码量进一步的减少。

来看看视图集的威力吧。

重构代码

大部分对接口的操作,都是在增删改查的基础上衍生出来的。既然这样, 视图集就将这些通用操作集成在一起了。

试下用视图集重构代码。

首先将之前写的与文章有关的序列化器都注释掉,新增一个提供给视图集的新序列化器:

# article/serializers.py ... class ArticleSerializer(serializers.HyperlinkedModelSerializer): author = UserDescSerializer(read_only=True) class Meta: model = Article fields = '__all__' ...

序列化器继承的 HyperlinkedModelSerializer 基本上与之前用的 ModelSerializer 差不多,区别是它自动提供了外键字段的超链接,并且默认不包含模型对象的 id 字段。

接着把之前写的文章视图也全注释掉,并新增代码:

# article/views.py ... from rest_framework import viewsets from article.serializers import ArticleSerializer class ArticleViewSet(viewsets.ModelViewSet): queryset = Article.objects.all() serializer_class = ArticleSerializer permission_classes = [IsAdminUserOrReadOnly] def perform_create(self, serializer): serializer.save(author=self.request.user)

视图集类把前面章节写的列表、详情等逻辑都集成到一起,并且提供了默认的增删改查的实现。

perform_create() 跟之前一样,在创建文章前,提供了视图集无法自行推断的用户外键字段。

由于使用了视图集,我们甚至连路由都不用自己设计了,使用框架提供的 Router 类就可以自动处理视图和 url 的连接。

修改项目根路由

# drf_vue_blog/urls.py ... from rest_framework.routers import DefaultRouter from article import views router = DefaultRouter() router.register(r'article', views.ArticleViewSet) urlpatterns = [ ... # drf 自动注册路由 path('api/', include(router.urls)), # article/urls.py 可以全注释掉,不需要了 # path('api/article/', include('article.urls', namespace='article')), ]

最后为了让分页更准确,给模型类规定好查询排序:

# article/models.py ... # 已经有的博客文章 model class Article(models.Model): ... class Meta: ordering = ['-created']

完成了。

平复下心情,烧两柱香,抱着试一试的心态打开命令行:(或浏览器)

C:\...> http http://127.0.0.1:8000/api/ HTTP/1.1 200 OK ... { "article": "http://127.0.0.1:8000/api/article/" }

Router 类送给我们一个接口导航!

顺着导航里给的链接再试试:

C:\...> http http://127.0.0.1:8000/api/article/ HTTP/1.1 200 OK ... { "count": 5, "next": "http://127.0.0.1:8000/api/article/?page=2", "previous": null, "results": [ { ... }, ... ] }

正确的显示了列表。再顺着列表提供的详情页点进去也肯定是没问题的。权限控制也与之前的完全一样。

就这么几行代码,就完成了一整套的接口操作!

鱼和熊掌

视图集最大程度地减少需要编写的代码量,并允许你专注于 API 提供的交互和表示形式,而不是 URL 的细节。但并不意味着用它总是比构建单独的视图更好。

精简可读之间,你应该根据实际情况进行取舍。

附加题

前几章用普通视图分别实现了列表和详情接口,并且不同的接口对应了不同的序列化器。

虽然视图集默认只提供一个序列化器,但是通过覆写 get_serializer_class() 方法可以根据条件而访问不同的序列化器:

class ArticleViewSet(viewsets.ModelViewSet): ... def get_serializer_class(self): if self.action == 'list': return SomeSerializer else: return AnotherSerializer

教程这里就暂时不去覆写了,后面有需要再来修改。

Last modified: 06 January 2025