Django drf ViewSets

2020-01-22 11:45 更新

ViewSets

在路由已经确定了用于请求的控制器之后,你的控制器负责理解请求并产生适当的输出。— Ruby on Rails 文档

Django REST framework允许你将一组相关视图的逻辑组合在单个类(称为 ViewSet)中。 在其他框架中,你也可以找到概念上类似于 'Resources' 或 'Controllers'的类似实现。

ViewSet 只是一种基于类的视图,它不提供任何方法处理程序(如 .get()或.post()),而是提供诸如 .list() 和 .create() 之类的操作。

ViewSet 的方法处理程序仅使用 .as_view() 方法绑定到完成视图的相应操作。

通常不是在 urlconf 中的视图集中显示注册视图,而是要使用路由类注册视图集,该类会自动为你确定 urlconf。

Example

让我们定义一个简单的视图集,可以用来列出或检索系统中的所有用户。

from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from myapps.serializers import UserSerializer
from rest_framework import viewsets
from rest_framework.response import Response

class UserViewSet(viewsets.ViewSet):
    """
    A simple ViewSet for listing or retrieving users.
    """
    def list(self, request):
        queryset = User.objects.all()
        serializer = UserSerializer(queryset, many=True)
        return Response(serializer.data)

    def retrieve(self, request, pk=None):
        queryset = User.objects.all()
        user = get_object_or_404(queryset, pk=pk)
        serializer = UserSerializer(user)
        return Response(serializer.data)

如果我们需要,我们可以将这个viewset绑定到两个单独的视图,想这样:

user_list = UserViewSet.as_view({'get': 'list'})
user_detail = UserViewSet.as_view({'get': 'retrieve'})

通常我们不会这么做,我们会用一个router来注册我们的viewset,让urlconf自动生成。

from myapp.views import UserViewSet
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r'users', UserViewSet)
urlpatterns = router.urls

不需要编写自己的视图集,你通常会想要使用提供默认行为的现有基类。例如:

class UserViewSet(viewsets.ModelViewSet):
    """
    用于查看和编辑用户实例的视图集。
    """
    serializer_class = UserSerializer
    queryset = User.objects.all()

与使用 View 类相比,使用 ViewSet 类有两个主要优点。

  • 重复的逻辑可以组合成一个类。在上面的例子中,我们只需要指定一次 queryset,它将在多个视图中使用。
  • 通过使用 routers, 哦们不再需要自己处理URLconf。

这两者都有一个权衡。使用常规的 views 和 URL confs 更明确也能够为你提供更多的控制。ViewSets有助于快速启动和运行,或者当你有大型的API,并且希望在整个过程中执行一致的 URL 配置。

标记路由的额外操作

REST framework 中包含的默任 routes 将为标准的 create/retrieve/update/destroy 类型操作提供路由, 如下所示:

class UserViewSet(viewsets.ViewSet):
    """
    示例 viewset 演示了将由路由器类处理的标准动作。

    如果你使用格式后缀,请务必为每个动作包含一个`format=None` 的关键字参数。
    """

    def list(self, request):
        pass

    def create(self, request):
        pass

    def retrieve(self, request, pk=None):
        pass

    def update(self, request, pk=None):
        pass

    def partial_update(self, request, pk=None):
        pass

    def destroy(self, request, pk=None):
        pass

如果你有需要被路由到的特别方法,你可以使用 @detail_route 或 @list_route 装饰器将它们标记为需要路由。

@detail_route 装饰器在其URL模式中包含 pk 用于需要单个实例的方法。The @list_route 装饰器用于对对象列表进行操作的方法。

例如:

from django.contrib.auth.models import User
from rest_framework import status
from rest_framework import viewsets
from rest_framework.decorators import detail_route, list_route
from rest_framework.response import Response
from myapp.serializers import UserSerializer, PasswordSerializer

class UserViewSet(viewsets.ModelViewSet):
    """
    一个提供标准动作的 viewset
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

    @detail_route(methods=['post'])
    def set_password(self, request, pk=None):
        user = self.get_object()
        serializer = PasswordSerializer(data=request.data)
        if serializer.is_valid():
            user.set_password(serializer.data['password'])
            user.save()
            return Response({'status': 'password set'})
        else:
            return Response(serializer.errors,
                            status=status.HTTP_400_BAD_REQUEST)

    @list_route()
    def recent_users(self, request):
        recent_users = User.objects.all().order('-last_login')

        page = self.paginate_queryset(recent_users)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(recent_users, many=True)
        return Response(serializer.data)

装饰器可以另外获取为路由视图设置的额外参数。例如...

    @detail_route(methods=['post'], permission_classes=[IsAdminOrIsSelf])
    def set_password(self, request, pk=None):
       ...

这些装饰器将默认路由 GET 请求,但也可以通过使用 methods 参数接受其他 HTTP 方法。例如:

    @detail_route(methods=['post', 'delete'])
    def unset_password(self, request, pk=None):
       ...

这两个新动作将在 urls ^users/{pk}/set_password/$ 和 ^users/{pk}/unset_password/$上可用。



以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号