一、地址
官方地址:
Viewsets - Django REST framework
相关文章:
二、ViewSets介绍
引入:
from rest_framework import viewsets
ViewSet 类只是一种类基础视图,它不提供任何方法处理程序(如 .get() 或 .post()),而是提供诸如 .list() 和 .create() 等操作。
ViewSet 的方法处理程序只在最终确定视图时与相应的操作绑定,使用 .as_view() 方法。
通常,您不需要在 urlconf 中显式注册 ViewSet 中的视图,而是将 ViewSet 注册到路由器类中,该类会自动确定 urlconf。
三、应用
3.1 视图
举例:
实现两个方法。
- list:列出所有用户的列表,
- retrieve:检索某一个用户的详细信息,
from rest_framework import viewsets
from rest_framework.response import Responseclass UserViewSet(viewsets.ViewSet):def list(self, request):# 从系统中获取所有用户queryset = User.objects.all()# 序列化查询集serializer = UserSerializer(queryset, many=True)# 将序列化的数据作为响应返回return Response(serializer.data)def retrieve(self, request, pk=None):# 根据主键(pk)获取系统中的单个用户try:user = User.objects.get(pk=pk)except User.DoesNotExist:# 处理用户不存在的情况return Response(status=status.HTTP_404_NOT_FOUND)# 序列化用户对象serializer = UserSerializer(user)# 将序列化的数据作为响应返回return Response(serializer.data)
在上面的示例中,我们定义了一个 UserViewSet
类,它继承自 viewsets.ViewSet
。在类内部,我们定义了两个方法:list()
和 retrieve()
。
list()
方法负责返回系统中所有用户的列表。它使用 User.objects.all()
查询集从数据库中获取所有用户对象,使用 UserSerializer
对查询集进行序列化,并将序列化的数据作为响应返回。
retrieve()
方法负责根据主键(pk)检索单个用户。它尝试根据提供的主键从数据库中获取用户对象。如果用户不存在,则返回 404 Not Found 响应。如果用户存在,则使用 UserSerializer
对用户对象进行序列化,并将序列化的数据作为响应返回。
您可以使用路由器将此 ViewSet 注册,以自动生成用于列出和检索用户的必要 URL。
3.2 路由
方式一:
如果需要的话,我们可以将这个 viewset 绑定到两个独立的视图中,像这样:
user_list = UserViewSet.as_view({'get': 'list'})
user_detail = UserViewSet.as_view({'get': 'retrieve'})
方式二:
通常情况下,我们不会这样做,而是使用路由器注册 viewset,并允许自动生成 URL 配置。
from myapp.views import UserViewSet
from rest_framework.routers import DefaultRouterrouter = DefaultRouter()
router.register(r'users', UserViewSet, basename='user')
urlpatterns = router.urls
与其编写自己的 viewset,通常更好的做法是使用现有的基类,这些基类提供了一组默认的行为。例如
class UserViewSet(viewsets.ModelViewSet):"""A viewset for viewing and editing user instances."""serializer_class = UserSerializerqueryset = User.objects.all()
使用 ViewSet 类而不是 View 类有两个主要优势。
首先,可以将重复的逻辑合并到一个类中。在上面的示例中,我们只需要指定一次查询集,它将在多个视图中使用。
其次,通过使用路由器,我们不再需要手动处理 URL 配置。
这两者都有一个权衡。使用常规的视图和 URL 配置更加明确,可以更好地控制。ViewSet 在您希望快速启动或具有大型 API 并且希望强制执行一致的 URL 配置时非常有用。
四、ViewSet actions
REST framework 中提供的默认路由器将为一组标准的创建/检索/更新/删除样式的操作提供路由,如下所示:
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 actions
在调度期间,ViewSet 上可以使用以下属性:
- basename - 用于创建 URL 名称的基础部分。
- action - 当前动作的名称(例如,list、create)。
- detail - 布尔值,指示当前动作是否为列表视图或详细视图配置的。
- suffix - 视图集类型的显示后缀 - 与 detail 属性相同。
- name - 视图集的显示名称。该参数与 suffix 互斥。
- description - 视图集中各个视图的显示描述。
您可以检查这些属性以根据当前动作调整行为。例如,您可以类似于以下示例将权限限制为除了 list 动作之外的所有权限:
def get_permissions(self):"""实例化并返回此视图所需的权限列表。"""if self.action == 'list':permission_classes = [IsAuthenticated]else:permission_classes = [IsAdminUser]return [permission() for permission in permission_classes]
以上示例中,get_permissions()
方法根据当前动作(self.action
)来确定要应用的权限类。如果当前动作是 list
,则使用 IsAuthenticated
权限类;否则,使用 IsAdminUser
权限类。最后,将权限类实例化并作为列表返回。
六、自定义actions
标记额外的操作以进行路由配置
在 ViewSet 中,您可以定义自定义的动作(除了默认的 create/retrieve/update/destroy 操作),并使用装饰器 @action
、@detail_route
或 @list_route
标记它们以进行路由配置。
from rest_framework.decorators import action
from rest_framework.response import Responseclass MyViewSet(viewsets.ModelViewSet):queryset = MyModel.objects.all()serializer_class = MySerializer# 自定义动作标记为 detail_route,表示它适用于详细视图@detail_route(methods=['post'])def custom_action(self, request, pk=None):instance = self.get_object()# 执行自定义操作# ...return Response({'message': 'Custom action executed successfully'})
上述示例中,custom_action
被标记为 @detail_route
,表示它是一个针对详细视图的自定义动作。您可以使用此装饰器指定不同的 HTTP 方法(如 methods=['post']
)和其他选项来配置路由和视图行为。
注意:在最新的版本中,推荐使用 @action
装饰器替代 @detail_route
和 @list_route
。例如,上面的示例可以改写为:
from rest_framework.decorators import action
from rest_framework.response import Responseclass MyViewSet(viewsets.ModelViewSet):queryset = MyModel.objects.all()serializer_class = MySerializer# 自定义动作标记为 action,不再需要指定方法(默认支持 GET)@action(detail=True, methods=['post'])def custom_action(self, request, pk=None):instance = self.get_object()# 执行自定义操作# ...return Response({'message': 'Custom action executed successfully'})
使用 @action
装饰器可以更清晰地指定一个动作,并且提供了更多的选项来配置路由和视图行为。
如果您有一些临时方法需要进行路由配置,可以使用@action
装饰器将它们标记为可路由的。与普通的操作一样,额外的操作可以针对单个对象或整个集合进行。为了指示这一点,将detail
参数设置为True
或False
。路由将相应地配置其URL模式,例如,DefaultRouter将在URL模式中包含pk
来配置详细操作。
以下是使用额外操作的更完整示例:
from rest_framework.decorators import action
from rest_framework.response import Responseclass MyViewSet(viewsets.ModelViewSet):queryset = MyModel.objects.all()serializer_class = MySerializer# 针对整个集合的额外操作@action(detail=False, methods=['get'])def custom_action_collection(self, request):# 在整个对象集合上执行自定义操作# ...return Response({'message': '在集合上成功执行自定义操作'})# 针对单个对象的额外操作@action(detail=True, methods=['get'])def custom_action_object(self, request, pk=None):instance = self.get_object()# 在单个对象上执行自定义操作# ...return Response({'message': '在对象上成功执行自定义操作'})
在上面的示例中,通过在@action
装饰器中设置detail=False
,我们将custom_action_collection
方法标记为整个集合的额外操作。此操作将在与ViewSet关联的URL模式上可访问,而无需任何对象标识符。
另一方面,custom_action_object
方法通过在@action
装饰器中设置detail=True
将其标记为单个对象的额外操作。此操作将在与ViewSet关联的URL模式上可访问,并且将包括对象标识符(例如,/mymodels/1/custom_action_object/
)。
通过使用@action
装饰器,您可以在ViewSet中定义和配置超出标准CRUD操作的额外操作。
@action
装饰器默认会路由GET请求,但也可以通过设置methods参数接受其他HTTP方法。例如:
@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方法映射到单独的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."""...
在上面的示例中,我们定义了名为"Change Password"的额外操作,并将其映射到PUT方法。在方法体内,我们可以执行更新用户密码的逻辑。
同时,我们使用.mapping.delete
装饰器将额外操作delete_password
映射到DELETE方法。在该方法内,我们可以执行删除用户密码的逻辑。
通过这种方式,您可以为额外操作定义不同的HTTP方法,以满足您的需求。
八、获取操作的URL
如果您需要获取操作的URL,请使用.reverse_action()方法。这是reverse()方法的一个便利包装,它会自动传递视图的请求对象,并在url_name之前添加.basename属性。
请注意,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。