Django 视图设置和路由_django

引言

​视图集​​是 Django REST Framework 中的工具,可以加速 API 开发。 它们是视图和 URL 之上的附加抽象层。 主要好处是单个视图集可以替换多个相关视图。 路由器可以自动为开发人员生成 URL。 在具有许多端点的大型项目中,这意味着开发人员必须编写更少的代码。 可以说,与一长串的单个视图和URL相比,对于经验丰富的开发人员而言,与少量视图集和路由器组合相比,它更易于理解和推理。

在本章中,我们将向现有项目中添加两个新的 API 端点,并了解如何从视图和 URL 切换到视图集和路由器可以用更少的代码实现相同的功能。

用户终端

当前,我们的项目中具有以下 API 端点。 它们都以 ​​api/v1/​​ 开头,为简洁起见,未显示它们:

前两个端点是我们创建的,而 django-rest-auth 提供了另外五个端点。 现在让我们添加两个其他端点,以列出所有用户和单个用户。 这是许多API中的常见功能,它将使我们更清楚地理解为什么将我们的视图和URL重构为视图集和路由器是有意义的。
传统 Django 具有内置的 User 模型类,我们已经在上一章中使用了该类进行身份验证。 因此,我们不需要创建新的数据库模型。 相反,我们只需要连接新的端点即可。 此过程始终涉及以下三个步骤:

  • 新增模型序列化器
  • 新增每个端点视图
  • 新增每个端点的URL路由

从我们的序列化器开始。 我们需要导入 User 模型,然后创建一个使用它的 UserSerializer 类。 然后将其添加到我们现有的 ​​posts/serializers.py​​文件中。


# posts/serializers.py
from django.contrib.auth import get_user_model # new
from rest_framework import serializers
from .models import Post


class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('id', 'author', 'title', 'body', 'created_at',)

class UserSerializer(serializers.ModelSerializer): # new
class Meta:
model = get_user_model()
fields = ('id', 'username',)

值得注意的是,虽然我们在这里使用get_user_model来引用User模型,但实际上在Django中有​​3种不同的方式​​来引用User模型。

通过使用 ​​get_user_model​​ ​,我们确保我们引用的是正确的用户模型,无论是默认用户模型还是新 Django项目中经常定义的​​自定义用户模型​​。

接下来,我们需要为每个端点定义视图。 首先将 UserSerializer 添加到导入列表中。 然后创建列出所有用户的 UserList 类和提供单个用户详细视图的 UserDetail 类。 就像我们的帖子视图一样,我们可以在此处使用 ​​ListCreateAPIView​​ ​ 和 ​​RetrieveUpdateDestroyAPIView​​ 。
对于每个端点,我们只需要只读或 GET 功能。 这意味着我们可以使用 ​​​ListAPIView​​  和 ​​RetrieveUpdateDestroyAPIView​​ 。 我们还需要通过 ​​get_user_model​​  引用用户模型,以便将其导入第一行。


# posts/views.py
from django.contrib.auth import get_user_model # new
from rest_framework import generics

from .models import Post
from .permissions import IsAuthorOrReadOnly
from .serializers import PostSerializer, UserSerializer # new


class PostList(generics.ListCreateAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer


class PostDetail(generics.RetrieveUpdateDestroyAPIView):
permission_classes = (IsAuthorOrReadOnly,)
queryset = Post.objects.all()
serializer_class = PostSerializer


class UserList(generics.ListCreateAPIView): # new
queryset = get_user_model().objects.all()
serializer_class = UserSerializer

class UserDetail(generics.RetrieveUpdateDestroyAPIView): # new
queryset = get_user_model().objects.all()
serializer_class = UserSerializer

如果您注意到,这里有很多重复。 Post 视图和 User 视图都具有完全相同的 queryset 和 serializer_class 。 也许可以通过某种方式组合起来以节省代码?
最后,我们有了 URL 路由。 确保导入新的 UserList 和 UserDetail 视图。 然后,我们可以为每个用户使用前缀 ​​​users/​​ 。

# posts/urls.py
from django.urls import path

from .views import UserList, UserDetail, PostList, PostDetail # new

urlpatterns = [
path('users/', UserList.as_view()), # new
path('users/<int:pk>/', UserDetail.as_view()), # new
path('', PostList.as_view()),
path('<int:pk>/', PostDetail.as_view()),
]

我们完成了。 确保本地服务器仍在运行,并跳至可浏览的API以确认一切正常。
我们的用户列表端点位于 ​​​http://127.0.0.1:8000/api/v1/users/​​ 

状态代码为 200 OK,表示一切正常。 我们可以看到三个现有用户。
每个用户的主键上都有一个用户详细信息终结点。 因此,我们的超级用户帐户位于: ​​​http://127.0.0.1:8000/api/v1/users/1/​​ 。

视图集

视图集是一种将多个相关视图的逻辑组合到单个类中的方法。 换句话说,一个视图集可以替换多个视图。 当前,我们有四个视图:两个用于博客帖子,两个用于用户。 相反,我们可以使用两个视图集来模仿相同的功能:一个用于博客文章,另一个用于用户。
折衷方案是,对于不十分熟悉视图集的其他开发人员,可读性会有所下降。 所以这是一个权衡。
当我们交换视图集时,代码在更新后的 ​​​posts/views.py​​ ​文件中是这样的。

# posts/views.py
from django.contrib.auth import get_user_model
from rest_framework import viewsets # new

from .models import Post
from .permissions import IsAuthorOrReadOnly
from .serializers import PostSerializer, UserSerializer


class PostViewSet(viewsets.ModelViewSet): # new
permission_classes = (IsAuthorOrReadOnly,)
queryset = Post.objects.all()
serializer_class = PostSerializer


class UserViewSet(viewsets.ModelViewSet): # new
queryset = get_user_model().objects.all()
serializer_class = UserSerializer

在顶部,而不是从rest_framework导入泛型,我们现在在第二行导入视图集。 然后,我们使用ModelViewSet,它为我们提供了列表视图和详细信息视图。 而且,我们不再需要像以前一样为每个视图重复相同的queryset和serializer_class!

Routers 路由

直接与视图集一起使用,以自动为我们生成URL模式。 我们当前的 ​​posts/urls.py​​ ​ 文件具有四个 URL模式:两个用于博客文章,两个用于用户。相反,我们可以为每个视图集采用一条路由。 因此,使用两个路由而不是四个 URL 模式。 听起来更好吧?
Django REST Framework 具有两个默认路由器: ​​​SimpleRouter​​ ​和 ​​DefaultRouter​​ ​。 我们将使用SimpleRouter,但也可以为更多高级功能创建自定义路由器。
更新后的代码如下所示:

# posts/urls.py
from django.urls import path
from rest_framework.routers import SimpleRouter

from .views import UserViewSet, PostViewSet

router = SimpleRouter()
router.register('users', UserViewSet, base_name='users') router.register('', PostViewSet, base_name='posts')

urlpatterns = router.urls

在最上面一行,将导入 SimpleRouter 及其视图。 路由器设置为 SimpleRouter,我们为用户和帖子“注册”每个视图集。 最后,我们将URL设置为使用新路由器。
继续并立即检查我们的四个端点! 用户列表是相同的。


但是,局部视图有些不同。 现在它被称为“用户实例”,而不是“用户详细信息”,并且还有一个附加的“删除”选项内置于​​​ModelViewSet​​​中。


可以自定义视图集,但是一个重要的折衷是用视图集编写更少的代码,这是默认设置,它可能需要一些其他配置才能完全匹配您想要的内容。
转到发布列表,我们可以看到它是相同的:

重要的是,我们的权限仍然有效。 使用我们的 testuser2 帐户登录时,发布实例为只读。


但是,如果我们使用超级用户帐户(该日志是单独的博客文章的作者)登录的,那么我们将具有完整的读写-编辑-删除权限。

总结

视图集和路由器是一种强大的抽象,可减少开发人员必须编写的代码量。 但是,这种简洁性是以牺牲初始学习曲线为代价的。 最初几次使用视图集和路由器而不是视图和 URL 模式会感到很奇怪。
最终,何时向项目添加视图集和路由器的决定是相当主观的。 一个好的经验法则是从视图和 URL 开始。 随着 API 复杂性的增加,如果您发现自己一遍又一遍地重复相同的端点模式,那么请查看视图集和路由器。 在此之前,请保持简单。