Django REST framework——ViewSets
Django REST框架允许您将一组相关视图的逻辑组合在一个类中,称为ViewSet。在其他框架中,您还可以找到类似“资源”或“控制器”之类的概念上类似的实现。
ViewSet类只是一种基于类的View,它不提供任何方法处理程序,如.get()或.post(),而是提供诸如.list()和.create()之类的操作。
ViewSet的方法处理程序仅使用.as_view()方法绑定到最终化视图时的相应操作。
通常,您不是在urlconf中的视图集中显式注册视图,而是使用路由器类注册视图集,该类会自动为您确定urlconf。
例子
让我们定义一个简单的viewset,可用于列出或检索系统中的所有用户。
from djano.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_objects_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'})
通常我们不会这样做,而是使用路由器注册viewset,并允许自动生成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 instance.
"""
serializer_class=UserSerializer
queryset=User.objects.all()
使用ViewSet类比使用View类有两个主要优点。
- 重复逻辑可以组合成一个类。在上面的示例中,我们只需要指定一次查询集,它将在多个视图中使用。
- 通过使用路由器,我们不再需要处理自己的URL连接。
这两者都需要权衡。使用常规视图和URL confs更明确,并为您提供更多控制。如果您希望快速启动并运行,或者您拥有大型API并希望在整个过程中强制执行一致的URL配置,则ViewSet非常有用。
ViewSet操作
REST框架中包含的默认路由器将为一组标准的create / retrieve / update / destroy样式操作提供路由,如下所示:
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 destory(self,request,pk=None):
pass
Introspecting ViewSet actions(反思ViewSet操作)
在调度期间,ViewSet上提供了以下属性。
- basename - 用于创建的URL名称的基础。
- action - 当前操作的名称(例如,list,create)。
- detail - boolean指示是否为列表或详细信息视图配置了当前操作。
- suffix - 视图集类型的显示后缀 - 镜像详细信息属性。
- name - 视图集的显示名称。该参数与后缀互斥。
- description - 视图集的各个视图的显示说明。
您可以检查这些属性以根据当前操作调整行为。例如,您可以限制除列表操作之外的所有内容的权限,类似于:
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]
Marking extra actions for routing(标记路由的额外操作)
如果你有可以路由的临时方法,你可以用@action装饰器标记它们。与常规操作一样,可以针对单个对象或整个集合执行额外操作。要指明这一点,请将detail参数设置为True或False。路由器将相应地配置其URL模式。例如,DefaultRouter将配置详细操作以在其URL模式中包含pk。
额外操作的更完整示例:
from djando.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(detail=True,methods=['post','delete'])
def unset_password(self,request,pk=None):
...
Routing additional HTTP methods for extra actions(为额外操作路由其他HTTP方法)
额外的操作可以将其他HTTP方法映射到单独的ViewSet方法。例如,上述密码设置/未设置方法可以合并为单个路由。请注意,其他映射不接受参数。
@action(detail=True,methods=['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."""
...
Reversing action URLs(撤消操作网址)
如果需要获取操作的URL,请使用.reverse_action()方法。这是reverse()的便捷包装器,自动传递视图的请求对象,并在url_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):
"""
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_classess=[IsAccountAdminOrReadOnly]
def get_queryset(self):
return self.request.user.account.all()
但请注意,从ViewSet中删除queryset属性后,任何关联的路由器都将无法自动派生模型的基本名称,因此您必须在路由器注册中指定basename kwarg。
另请注意,虽然此类默认提供完整的create/list/retrieve/update/destroy操作集,但您可以使用标准权限类来限制可用操作。
ReadOnlyModelViewSet
ReadOnlyModelViewSet类也继承自GenericAPIView。与ModelViewSet一样,它还包括各种操作的实现,但与ModelViewSet不同,它只提供“只读”操作,.list()和.retrieve()。
例子
与ModelViewSet一样,您通常需要至少提供queryset和serializer_class属性。例如:
class AccountViewSet(viewsets.ReadOnlyModelViewSet):
"""
A simple ViewSet for viewing accounts
"""
queryset=Account.objects.all()
serializer_class=AccountSerializer
同样,与ModelViewSet一样,您可以使用GenericAPIView可用的任何标准属性和方法覆盖。
Custom ViewSet base classes(自定义ViewSet基类)
您可能需要提供自定义ViewSet类,这些类没有完整的ModelViewSet操作集,或者以其他方式自定义行为。
例子
要创建提供创建,列表和检索操作的基本视图集类,请继承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中的多个视图集中重用的常见行为。