在路由确定了要用于请求的控制器之后,您的控制器负责理解请求并产生适当的输出。
— Ruby on Rails文档
Django REST框架允许您在称为的单个类中为一组相关视图组合逻辑ViewSet
。在其他框架中,您可能还会发现概念上类似的实现,它们的名称类似于“资源”或“控制器”。
一个ViewSet
类只是基于类的View的一种,它不提供诸如.get()
或的任何方法处理程序.post()
,而是提供诸如.list()
和的操作.create()
。
使用方法,a的方法处理程序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, basename='user')
urlpatterns = router.urls
您通常不希望编写自己的视图集,而是要使用提供默认行为集的现有基类。例如:
class UserViewSet(viewsets.ModelViewSet):
"""
A viewset for viewing and editing user instances.
"""
serializer_class = UserSerializer
queryset = User.objects.all()
与使用ViewSet
类相比,使用类有两个主要优点View
。
- 重复的逻辑可以合并为一个类。在上面的示例中,我们只需要指定
queryset
一次,它将在多个视图中使用。 - 通过使用路由器,我们不再需要自己处理URL conf。
两者都需要权衡。使用常规视图和URL confs更明确,并给您更多控制权。如果您想快速启动并运行,或者当您拥有大型API并希望在整个过程中实施一致的URL配置,则ViewSets很有帮助。
ViewSet操作
REST框架随附的默认路由器将为一组标准的创建/检索/更新/销毁样式操作提供路由,如下所示:
class UserViewSet(viewsets.ViewSet):
"""
Example empty viewset demonstrating the standard
actions that will be handled by a router class.
If you're using format suffixes, make sure to also include
the `format=None` keyword argument for each action.
"""
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动作
在分发期间,可以使用以下属性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 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):
"""
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.delete
def 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()
。
例
由于ModelViewSet
extends GenericAPIView
,通常需要至少提供queryset
和serializer_class
属性。例如:
class AccountViewSet(viewsets.ModelViewSet):
"""
A simple ViewSet for viewing and editing accounts.
"""
queryset = Account.objects.all()
serializer_class = AccountSerializer
permission_classes = [IsAccountAdminOrReadOnly]
请注意,您可以使用所提供的任何标准属性或方法替代GenericAPIView
。例如,要使用ViewSet
动态确定其应操作的查询集的,您可以执行以下操作:
class AccountViewSet(viewsets.ModelViewSet):
"""
A simple ViewSet for viewing and editing the accounts
associated with the user.
"""
serializer_class = AccountSerializer
permission_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
,您通常至少需要提供queryset
andserializer_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 mixins
class 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的多个视图集中重用的常见行为。