在路由确定了要用于请求的控制器之后,您的控制器负责理解请求并产生适当的输出。

— Ruby on Rails文档

Django REST框架允许您在称为的单个类中为一组相关视图组合逻辑ViewSet。在其他框架中,您可能还会发现概念上类似的实现,它们的名称类似于“资源”或“控制器”。

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

使用方法,a的方法处理程序ViewSet仅在完成视图时绑定到相应的动作.as_view()

通常,您将在路由器类中注册视图集,而不是在urlconf中的视图集中显式注册视图,而是自动为您确定urlconf。

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

  1. from django.contrib.auth.models import User
  2. from django.shortcuts import get_object_or_404
  3. from myapps.serializers import UserSerializer
  4. from rest_framework import viewsets
  5. from rest_framework.response import Response
  6. class UserViewSet(viewsets.ViewSet):
  7. """
  8. A simple ViewSet for listing or retrieving users.
  9. """
  10. def list(self, request):
  11. queryset = User.objects.all()
  12. serializer = UserSerializer(queryset, many=True)
  13. return Response(serializer.data)
  14. def retrieve(self, request, pk=None):
  15. queryset = User.objects.all()
  16. user = get_object_or_404(queryset, pk=pk)
  17. serializer = UserSerializer(user)
  18. return Response(serializer.data)

如果需要,可以将此视图集绑定到两个单独的视图中,如下所示:

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

通常,我们不这样做,而是向路由器注册视图集,并允许urlconf自动生成。

  1. from myapp.views import UserViewSet
  2. from rest_framework.routers import DefaultRouter
  3. router = DefaultRouter()
  4. router.register(r'users', UserViewSet, basename='user')
  5. urlpatterns = router.urls

您通常不希望编写自己的视图集,而是要使用提供默认行为集的现有基类。例如:

  1. class UserViewSet(viewsets.ModelViewSet):
  2. """
  3. A viewset for viewing and editing user instances.
  4. """
  5. serializer_class = UserSerializer
  6. queryset = User.objects.all()

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

  • 重复的逻辑可以合并为一个类。在上面的示例中,我们只需要指定queryset一次,它将在多个视图中使用。
  • 通过使用路由器,我们不再需要自己处理URL conf。

两者都需要权衡。使用常规视图和URL confs更明确,并给您更多控制权。如果您想快速启动并运行,或者当您拥有大型API并希望在整个过程中实施一致的URL配置,则ViewSets很有帮助。

ViewSet操作

REST框架随附的默认路由器将为一组标准的创建/检索/更新/销毁样式操作提供路由,如下所示:

  1. class UserViewSet(viewsets.ViewSet):
  2. """
  3. Example empty viewset demonstrating the standard
  4. actions that will be handled by a router class.
  5. If you're using format suffixes, make sure to also include
  6. the `format=None` keyword argument for each action.
  7. """
  8. def list(self, request):
  9. pass
  10. def create(self, request):
  11. pass
  12. def retrieve(self, request, pk=None):
  13. pass
  14. def update(self, request, pk=None):
  15. pass
  16. def partial_update(self, request, pk=None):
  17. pass
  18. def destroy(self, request, pk=None):
  19. pass

内省ViewSet动作

在分发期间,可以使用以下属性ViewSet

  • basename -用于创建的URL名称的基础。
  • action-当前动作的名称(例如listcreate)。
  • detail -布尔值,指示是否为列表视图或详细信息视图配置了当前操作。
  • suffix-视图集类型的显示后缀-镜像detail属性。
  • name-视图集的显示名称。此参数与互斥suffix
  • description -视图集单个视图的显示描述。

您可以检查这些属性以根据当前操作调整行为。例如,您可以将权限限制为除以下list类似操作外的所有权限:

  1. def get_permissions(self):
  2. """
  3. Instantiates and returns the list of permissions that this view requires.
  4. """
  5. if self.action == 'list':
  6. permission_classes = [IsAuthenticated]
  7. else:
  8. permission_classes = [IsAdmin]
  9. return [permission() for permission in permission_classes]

标记用于路由的其他操作

如果您具有可路由的临时方法,则可以使用@action装饰器将它们标记为可路由的。像常规操作一样,额外的操作可能打算用于单个对象或整个集合。为了表明这一点,请将detail参数设置为TrueFalse。路由器将相应地配置其URL模式。例如,DefaultRouter将配置详细信息操作以包含pk在其URL模式中。

额外操作的更完整示例:

  1. from django.contrib.auth.models import User
  2. from rest_framework import status, viewsets
  3. from rest_framework.decorators import action
  4. from rest_framework.response import Response
  5. from myapp.serializers import UserSerializer, PasswordSerializer
  6. class UserViewSet(viewsets.ModelViewSet):
  7. """
  8. A viewset that provides the standard actions
  9. """
  10. queryset = User.objects.all()
  11. serializer_class = UserSerializer
  12. @action(detail=True, methods=['post'])
  13. def set_password(self, request, pk=None):
  14. user = self.get_object()
  15. serializer = PasswordSerializer(data=request.data)
  16. if serializer.is_valid():
  17. user.set_password(serializer.data['password'])
  18. user.save()
  19. return Response({'status': 'password set'})
  20. else:
  21. return Response(serializer.errors,
  22. status=status.HTTP_400_BAD_REQUEST)
  23. @action(detail=False)
  24. def recent_users(self, request):
  25. recent_users = User.objects.all().order_by('-last_login')
  26. page = self.paginate_queryset(recent_users)
  27. if page is not None:
  28. serializer = self.get_serializer(page, many=True)
  29. return self.get_paginated_response(serializer.data)
  30. serializer = self.get_serializer(recent_users, many=True)
  31. return Response(serializer.data)

action装饰后,系统会GET默认请求,也可以接受设定其他HTTP方法methods的参数。例如:

  1. @action(detail=True, methods=['post', 'delete'])
  2. def unset_password(self, request, pk=None):
  3. ...

装饰器允许您覆盖任何视图集中级别的配置,例如permission_classesserializer_classfilter_backends…:

  1. @action(detail=True, methods=['post'], permission_classes=[IsAdminOrIsSelf])
  2. def set_password(self, request, pk=None):
  3. ...

这两个新操作将在url^users/{pk}/set_password/$和处可用^users/{pk}/unset_password/$。使用url_pathurl_name参数更改操作的URL段和反向URL名称。

要查看所有其他操作,请调用.get_extra_actions()方法。

路由其他HTTP方法以执行其他操作

额外的操作可以将其他HTTP方法映射到单独的ViewSet方法。例如,上述密码设置/取消方法可以合并为一条路由。请注意,其他映射不接受参数。

  1. @action(detail=True, methods=['put'], name='Change Password')
  2. def password(self, request, pk=None):
  3. """Update the user's password."""
  4. ...
  5. @password.mapping.delete
  6. def delete_password(self, request, pk=None):
  7. """Delete the user's password."""
  8. ...

反向操作网址

如果需要获取操作的URL,请使用.reverse_action()方法。这是的便利包装,用于reverse()自动传递视图的request对象并url_name.basename属性之前添加。

请注意,basename是由路由器在ViewSet注册过程中提供的。如果不使用路由器,则必须basename为该.as_view()方法提供参数。

使用上一节中的示例:

  1. >>> view.reverse_action('set-password', args=['1'])
  2. 'http://localhost:8000/api/users/1/set_password'

或者,您可以使用装饰器url_name设置的属性@action

  1. >>> view.reverse_action(view.set_password.url_name, args=['1'])
  2. 'http://localhost:8000/api/users/1/set_password'

url_name参数.reverse_action()应与@action装饰器匹配相同的参数。此外,此方法可用于撤消默认操作,例如listcreate


API参考

视图集

ViewSet类从继承APIView。您可以使用任何标准的属性,如permission_classesauthentication_classes以控制在视图集的API政策。

ViewSet类不提供任何操作实现。为了使用一个ViewSet类,您将覆盖该类并显式定义动作实现。

通用视图集

GenericViewSet从类继承GenericAPIView,并提供了默认设置get_objectget_queryset方法及其他通用视图基地的行为,但不包括默认情况下,任何动作。

为了使用一个GenericViewSet类,您将覆盖该类并混合所需的mixin类,或显式定义操作实现。

模型视图集

ModelViewSet从类继承GenericAPIView,并包括用于各种动作实现方式中,通过在各种混入类的行为混合。

由提供的动作ModelViewSet类是.list().retrieve().create().update().partial_update(),和.destroy()

由于ModelViewSetextends GenericAPIView,通常需要至少提供querysetserializer_class属性。例如:

  1. class AccountViewSet(viewsets.ModelViewSet):
  2. """
  3. A simple ViewSet for viewing and editing accounts.
  4. """
  5. queryset = Account.objects.all()
  6. serializer_class = AccountSerializer
  7. permission_classes = [IsAccountAdminOrReadOnly]

请注意,您可以使用所提供的任何标准属性或方法替代GenericAPIView。例如,要使用ViewSet动态确定其应操作的查询集的,您可以执行以下操作:

  1. class AccountViewSet(viewsets.ModelViewSet):
  2. """
  3. A simple ViewSet for viewing and editing the accounts
  4. associated with the user.
  5. """
  6. serializer_class = AccountSerializer
  7. permission_classes = [IsAccountAdminOrReadOnly]
  8. def get_queryset(self):
  9. return self.request.user.accounts.all()

但是请注意,queryset从您的属性中删除该属性后ViewSet,任何关联的路由器将无法自动派生Model的基本名称,因此您必须basename路由器注册中指定kwarg 。

还要注意,尽管默认情况下此类提供了完整的create / list / retrieve / update / destroy操作集,但是您可以使用标准权限类来限制可用的操作。

ReadOnlyModelViewSet

ReadOnlyModelViewSet班也继承GenericAPIView。与一样,ModelViewSet它也包含各种动作的实现,但与ModelViewSet仅提供“只读”动作不同,.list().retrieve()

与一样ModelViewSet,您通常至少需要提供querysetandserializer_class属性。例如:

  1. class AccountViewSet(viewsets.ReadOnlyModelViewSet):
  2. """
  3. A simple ViewSet for viewing accounts.
  4. """
  5. queryset = Account.objects.all()
  6. serializer_class = AccountSerializer

同样,ModelViewSet您可以使用的任何标准属性和方法替代GenericAPIView

自定义ViewSet基类

您可能需要提供ViewSet没有完整ModelViewSet动作集或以其他方式自定义行为的自定义类。

要创建基础视图集类,提供createlistretrieve操作,继承GenericViewSet和混入所需的操作:

  1. from rest_framework import mixins
  2. class CreateListRetrieveViewSet(mixins.CreateModelMixin,
  3. mixins.ListModelMixin,
  4. mixins.RetrieveModelMixin,
  5. viewsets.GenericViewSet):
  6. """
  7. A viewset that provides `retrieve`, `create`, and `list` actions.
  8. To use it, override the class and set the `.queryset` and
  9. `.serializer_class` attributes.
  10. """
  11. pass

通过创建自己的基ViewSet类,您可以提供可以在您的API的多个视图集中重用的常见行为。