【djangorestframework】7、ViewSets(视图集)

ViewSets

  • 在路由确定了用于请求的控制器之后,控制器负责了解请求并生成适当的输出。--Ruby on Rails文档
  • Django REST framework允许你将一组相关视图的逻辑组合在一个单独的类中,成为ViewSet。在其他框架中,你可能会发现概念上类似的出现,名为"Resources"或"Controllers"
  • ViewSet类只是一种基于类的视图类型,它不提供任何方法处理程序,如.get()或.post();而是提供诸如.list()和.create()之类的操作
  • ViewSet的方法处理程序使用.as_view()方法在最终确定视图时绑定相应操作
  • 通常,与其在urlconf中显式地注册视图集中的视图,倒不如由路由器类注册视图集,它自动为你确定urlconf
举个例子:
  • 让我们定义一个简单的视图集,可用于列出或检索系统中的所有用户。
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)
  • 如果需要,我们可以将此视图集绑定到两个单独的视图中,如下所示:
user_list = UserViewSet.as_view({'get': 'list'})
user_detail = UserViewSet.as_view({'get': 'retrieve'})
  • 通常我们不会这样做,而是用一个路由器注册视图集,并允许自动生成urlconf
from myapp.views import UserViewSet
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r'users', UserViewSet, base_name='user')
urlpatterns = router.urls
  • 你通常希望使用提供默认行为集合的现有基类,而不是编写自己的视图集。举个例子:
class UserViewSet(viewsets.ModelViewSet):
    """
    用于查看和编辑用户实例的视图集。
    """
    serializer_class = UserSerializer
    queryset = User.objects.all()
  • 使用ViewSet类比使用View类有两个主要优点:
    • 重复的逻辑可以组合成一个类。在上面的示例中,我们只需要指定一次queryset,它将在多个视图中使用
    • 通过使用routers,我们不再需要处理自己的URL连接
  • 这两者各有优缺点。使用常规视图和URL配置文件更加明确,并为你提供更多控制。如果你想快速启动和运行,或者当你拥有大型API并希望始终执行一致的URL配置时,ViewSets非常有用。

视图集操作

  • REST framework中包含的默认路由器将为一套标准的创建/检索/更新/销毁样式操作提供路由,如下所示:
class UserViewSet(viewsets.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

反思视图集操作

  • 在调度期间,ViewSet上提供了以下属性。
    • basename---用于创建的URL名称的基础
    • action---当前操作的名称(例如:list,create)
    • detail---布尔值指示当前动作是否配置为列表或详细视图
    • suffix---视图集类型的显示后缀-反射详细属性
  • 你可以检查这些属性以根据当前操作调整行为。例如,你可以限制除了与list类似的所有权限:
def get_permissions(self):
    """
    实例化并返回该视图所需的权限列表。
    """
    if self.action == 'list':
        permission_classes = [IsAuthenticated]
    else:
        permission_classes = [IsAdmin]
    return [permission() for permission in permission_classes]

标记路由的额外操作

  • 如果你有可路由的特殊方法,你可以用@action装饰器来标记它们。与常规操作一样,额外的操作可以用于对象列表或单个实例。要指出这一点,设置detail参数为True或False。路由器将相应地配置其URL模式。例如,DefaultRouter将在其URL模式中配置包含pk的详细操作。
  • 更完整的额外操作例子:
from django.contrib.auth.models import User
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from myapp.serializers import UserSerializer, PasswordSerializer

class UserViewSet(viewsets.ModelViewSet):
    """
    提供标准操作的视图集
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

    @action(methods=['post'], detail=True)
    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)

    @action(detail=False)
    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)
  • 另外,装饰器可以为路由设置额外的参数。举个例子:
@action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf])
def set_password(self, request, pk=None):
    ...
  • 这些装饰器将默认路由GET请求,但也可以通过设置methods参数来接受其他HTTP方法。例如:
@action(methods=['post', 'delete'], detail=True)
def unset_password(self, request, pk=None):
   ...
  • 两个新的操作将在url ^users/{pk}/set_password/$和^users/{pk}/unset_password/$上可用。
  • 要查看所有额外操作,请调用.get_extra_actions()方法

反转操作urls

  • 如果你需要获取操作的URL,请使用.reverse_action()方法。这是Reverse()的便利封装,它自动传递传递视图的request对象,并在url_name前加上.base_name属性。
  • 请注意,basename是路由器在ViewSet注册期间提供的。如果不适用路由器,则必须向.as_view()方法提供basename参数。
  • 使用上一节中的示例:
>>> view.reverse_action('set-password', args=['1'])
'http://localhost:8000/api/users/1/set_password'
  • 或者,你可以使用@action装饰器设置的url_name属性
>>> view.reverse_action(view.set_password.url_name, args=['1'])
'http://localhost:8000/api/users/1/set_password'
  • .reverse_action()的url_name参数应该与@action装饰器匹配相同的参数。此外,此方法可用于反转默认操作,例如list和create.

API参考

ViewSet

  • ViewSet类继承自APIView。你可以使用任何标准属性(例如permission_classes,authentication_classes)来控制视图集上的API策略。
  • ViewSet类不提供任何操作的实现。为了使用ViewSet类,你将重写该类并显式地定义操作的实现。

GenericViewSet

  • GenericViewSet类继承自GenericAPIView,并提供默认的get_object,get_queryset方法和其他通用视图基本行为,但默认情况下不包含任何操作
  • 为了使用GenericViewSet类,你将重写该类,或混合所需的mixin类,或者显式定义操作实现。

ModelViewSet

  • ModelViewSet类继承自GenericAPIView,并通过混合各种mixin类的行为来包含各种操作的实现
  • ModelViewSet类提供的操作有.list(),.retrieve(), .create(),.update(),.partial_update()和.destroy().
  • 举个例子:
    • 因为ModelViewSet扩展了GenericAPIView,所以通常需要至少提供queryset和serializer_class属性。
    • 例如:
class AccountViewSet(viewsets.ModelViewSet):
    """
    用于查看和编辑帐户的简单 ViewSet。
    """
    queryset = Account.objects.all()
    serializer_class = AccountSerializer
    permission_classes = [IsAccountAdminOrReadOnly]
  • 注意,你可以使用GenericAPIView提供的任何标准属性或方法重写。例如,要动态确定它应该操作的查询集的ViewSet,可以这样做:
class AccountViewSet(viewsets.ModelViewSet):
    """
    一个简单的视图集,用于查看和编辑与用户相关的帐户。
    """
    serializer_class = AccountSerializer
    permission_classes = [IsAccountAdminOrReadOnly]

    def get_queryset(self):
        return self.request.user.accounts.all()
  • 然而注意,当从你的ViewSet中移除queryset属性,任何关联的路由器将无法自动导出模型的base_name,因此你必须制定base_name kwarg作为路由器注册的一部分。
  • 还要注意,尽管这个类默认情况下提供了完整的创建/列表/检索/更新/销毁操作集,但是可以使用标准权限类来限制可用操作。

ReadOnlyModelViewSet

  • ReadOnlyModelViewSet类也继承自GenericAPIView。与ModelViewSet一样,它还包括各种操作的实现,但与ModelViewSet不同,它只提供"只读"操作,.list()和.retrieve()
  • 举个例子
    • 与modelViewSet一样,你通常需要至少提供queryset和serializer_class属性。例如:
class AccountViewSet(viewsets.ReadOnlyModelViewSet):
    """
    用于查看帐户的简单 ViewSet。
    """
    queryset = Account.objects.all()
    serializer_class = AccountSerializer
  • 同样,与ModelViewSet一样,你可以使用GenericAPIView可用的任何标准属性和方法重写。

自定义视图集基类

  • 你可能需要提供自定义ViewSet类,这些类没有完整的ModelViewSet操作集,或者以其他方式自定义行为。
  • 举个例子:
    • 要创建提供create,list和retrieve操作的基本视图集类,请继承GenericViewSet,并混合所需的操作:
rom rest_framework import mixins

class CreateListRetrieveViewSet(mixins.CreateModelMixin,
                                mixins.ListModelMixin,
                                mixins.RetrieveModelMixin,
                                viewsets.GenericViewSet):
    """
    提供“检索”、“创建”和“列表”操作的视图集。

    若要使用它,请重写该类并设置 “.queryset” 和 “.serializer_class” 属性。
    """
    pass
  • 通过创建自己的基本ViewSet类,可以提供在API中的多个视图集中重用的通用行为。
posted @ 2022-04-25 16:00  郭祺迦  阅读(321)  评论(0)    收藏  举报