在路由确定了要用于请求的控制器之后,您的控制器负责理解请求并产生适当的输出。
— Ruby on Rails文档
Django REST框架允许您在称为的单个类中为一组相关视图组合逻辑ViewSet。在其他框架中,您可能还会发现概念上类似的实现,它们的名称类似于“资源”或“控制器”。
一个ViewSet类只是基于类的View的一种,它不提供诸如.get()或的任何方法处理程序.post(),而是提供诸如.list()和的操作.create()。
使用方法,a的方法处理程序ViewSet仅在完成视图时绑定到相应的动作.as_view()。
通常,您将在路由器类中注册视图集,而不是在urlconf中的视图集中显式注册视图,而是自动为您确定urlconf。
例
让我们定义一个简单的视图集,可用于列出或检索系统中的所有用户。
from django.contrib.auth.models import Userfrom django.shortcuts import get_object_or_404from myapps.serializers import UserSerializerfrom rest_framework import viewsetsfrom rest_framework.response import Responseclass 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 UserViewSetfrom rest_framework.routers import DefaultRouterrouter = DefaultRouter()router.register(r'users', UserViewSet, basename='user')urlpatterns = router.urls
您通常不希望编写自己的视图集,而是要使用提供默认行为集的现有基类。例如:
class UserViewSet(viewsets.ModelViewSet):"""A viewset for viewing and editing user instances."""serializer_class = UserSerializerqueryset = User.objects.all()
与使用ViewSet类相比,使用类有两个主要优点View。
- 重复的逻辑可以合并为一个类。在上面的示例中,我们只需要指定
queryset一次,它将在多个视图中使用。 - 通过使用路由器,我们不再需要自己处理URL conf。
两者都需要权衡。使用常规视图和URL confs更明确,并给您更多控制权。如果您想快速启动并运行,或者当您拥有大型API并希望在整个过程中实施一致的URL配置,则ViewSets很有帮助。
ViewSet操作
REST框架随附的默认路由器将为一组标准的创建/检索/更新/销毁样式操作提供路由,如下所示:
class UserViewSet(viewsets.ViewSet):"""Example empty viewset demonstrating the standardactions that will be handled by a router class.If you're using format suffixes, make sure to also includethe `format=None` keyword argument for each action."""def list(self, request):passdef create(self, request):passdef retrieve(self, request, pk=None):passdef update(self, request, pk=None):passdef partial_update(self, request, pk=None):passdef destroy(self, request, pk=None):pass
内省ViewSet动作
在分发期间,可以使用以下属性ViewSet。
basename-用于创建的URL名称的基础。action-当前动作的名称(例如list,create)。detail-布尔值,指示是否为列表视图或详细信息视图配置了当前操作。suffix-视图集类型的显示后缀-镜像detail属性。name-视图集的显示名称。此参数与互斥suffix。description-视图集单个视图的显示描述。
您可以检查这些属性以根据当前操作调整行为。例如,您可以将权限限制为除以下list类似操作外的所有权限:
def get_permissions(self):"""Instantiates and returns the list of permissions that this view requires."""if self.action == 'list':permission_classes = [IsAuthenticated]else:permission_classes = [IsAdmin]return [permission() for permission in permission_classes]
标记用于路由的其他操作
如果您具有可路由的临时方法,则可以使用@action装饰器将它们标记为可路由的。像常规操作一样,额外的操作可能打算用于单个对象或整个集合。为了表明这一点,请将detail参数设置为True或False。路由器将相应地配置其URL模式。例如,DefaultRouter将配置详细信息操作以包含pk在其URL模式中。
额外操作的更完整示例:
from django.contrib.auth.models import Userfrom rest_framework import status, viewsetsfrom rest_framework.decorators import actionfrom rest_framework.response import Responsefrom myapp.serializers import UserSerializer, PasswordSerializerclass UserViewSet(viewsets.ModelViewSet):"""A viewset that provides the standard actions"""queryset = User.objects.all()serializer_class = UserSerializer@action(detail=True, 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)@action(detail=False)def recent_users(self, request):recent_users = User.objects.all().order_by('-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装饰后,系统会GET默认请求,也可以接受设定其他HTTP方法methods的参数。例如:
@action(detail=True, methods=['post', 'delete'])def unset_password(self, request, pk=None):...
装饰器允许您覆盖任何视图集中级别的配置,例如permission_classes,serializer_class,filter_backends…:
@action(detail=True, methods=['post'], permission_classes=[IsAdminOrIsSelf])def set_password(self, request, pk=None):...
这两个新操作将在url^users/{pk}/set_password/$和处可用^users/{pk}/unset_password/$。使用url_path和url_name参数更改操作的URL段和反向URL名称。
要查看所有其他操作,请调用.get_extra_actions()方法。
路由其他HTTP方法以执行其他操作
额外的操作可以将其他HTTP方法映射到单独的ViewSet方法。例如,上述密码设置/取消方法可以合并为一条路由。请注意,其他映射不接受参数。
@action(detail=True, methods=['put'], name='Change Password')def password(self, request, pk=None):"""Update the user's password."""...@password.mapping.deletedef delete_password(self, request, pk=None):"""Delete the user's password."""...
反向操作网址
如果需要获取操作的URL,请使用.reverse_action()方法。这是的便利包装,用于reverse()自动传递视图的request对象并url_name在.basename属性之前添加。
请注意,basename是由路由器在ViewSet注册过程中提供的。如果不使用路由器,则必须basename为该.as_view()方法提供参数。
使用上一节中的示例:
>>> view.reverse_action('set-password', args=['1'])'http://localhost:8000/api/users/1/set_password'
或者,您可以使用装饰器url_name设置的属性@action。
>>> view.reverse_action(view.set_password.url_name, args=['1'])'http://localhost:8000/api/users/1/set_password'
的url_name参数.reverse_action()应与@action装饰器匹配相同的参数。此外,此方法可用于撤消默认操作,例如list和create。
API参考
视图集
将ViewSet类从继承APIView。您可以使用任何标准的属性,如permission_classes,authentication_classes以控制在视图集的API政策。
本ViewSet类不提供任何操作实现。为了使用一个ViewSet类,您将覆盖该类并显式定义动作实现。
通用视图集
在GenericViewSet从类继承GenericAPIView,并提供了默认设置get_object,get_queryset方法及其他通用视图基地的行为,但不包括默认情况下,任何动作。
为了使用一个GenericViewSet类,您将覆盖该类并混合所需的mixin类,或显式定义操作实现。
模型视图集
在ModelViewSet从类继承GenericAPIView,并包括用于各种动作实现方式中,通过在各种混入类的行为混合。
由提供的动作ModelViewSet类是.list(),.retrieve(), .create(),.update(),.partial_update(),和.destroy()。
例
由于ModelViewSetextends GenericAPIView,通常需要至少提供queryset和serializer_class属性。例如:
class AccountViewSet(viewsets.ModelViewSet):"""A simple ViewSet for viewing and editing accounts."""queryset = Account.objects.all()serializer_class = AccountSerializerpermission_classes = [IsAccountAdminOrReadOnly]
请注意,您可以使用所提供的任何标准属性或方法替代GenericAPIView。例如,要使用ViewSet动态确定其应操作的查询集的,您可以执行以下操作:
class AccountViewSet(viewsets.ModelViewSet):"""A simple ViewSet for viewing and editing the accountsassociated with the user."""serializer_class = AccountSerializerpermission_classes = [IsAccountAdminOrReadOnly]def get_queryset(self):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属性。例如:
class AccountViewSet(viewsets.ReadOnlyModelViewSet):"""A simple ViewSet for viewing accounts."""queryset = Account.objects.all()serializer_class = AccountSerializer
同样,ModelViewSet您可以使用的任何标准属性和方法替代GenericAPIView。
自定义ViewSet基类
您可能需要提供ViewSet没有完整ModelViewSet动作集或以其他方式自定义行为的自定义类。
例
要创建基础视图集类,提供create,list和retrieve操作,继承GenericViewSet和混入所需的操作:
from rest_framework import mixinsclass CreateListRetrieveViewSet(mixins.CreateModelMixin,mixins.ListModelMixin,mixins.RetrieveModelMixin,viewsets.GenericViewSet):"""A viewset that provides `retrieve`, `create`, and `list` actions.To use it, override the class and set the `.queryset` and`.serializer_class` attributes."""pass
通过创建自己的基ViewSet类,您可以提供可以在您的API的多个视图集中重用的常见行为。
