ViewSets
文章目录
1. 简介
Django REST framework 中的 ViewSet
允许将一组相关视图的逻辑组合在一个类中。它与其他框架中类似的概念,如“Resources”或“Controllers”相似。
ViewSet
类与普通基于类的视图不同,它不直接提供像 get()
或 post()
这样的方法处理程序,而是提供了诸如 list()
、create()
等动作。
ViewSet
的方法处理程序只有在使用 as_view()
方法最终确定视图时,才与相应的动作绑定。
通常,不是在 URL 配置中显式地注册 ViewSet
中的视图,**而是使用路由器类来注册 ViewSe**t
,路由器类会自动为您确定 URL 配置。
例如:
from rest_framework import viewsets
from myapp.models import MyModel
from myapp.serializers import MyModelSerializer
class MyModelViewSet(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
然后通过路由器来处理 URL 映射:
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'mymodels', MyModelViewSet)
from django.urls import include, path
urlpatterns = [
path('', include(router.urls)),
# 其他 URL 模式...
]
2.viewset
示例: 列出系统中的用户
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'})
但是可以使用rest_framework.routers
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()
相比较view和viewset:
- 重复的逻辑可以组合成一个类
- 通过使用路由器,不需要自己处理url的配置问题
这两者都需要权衡取舍。使用常规视图和 URL confs 更明确,并为您提供更多控制权。如果您想快速启动并运行,或者当您有一个大型 API 并且希望在整个过程中强制执行一致的 URL 配置时,ViewSet 非常有用。
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
Introspecting ViewSet actions
以下是关于 ViewSet
内省(Introspecting)操作的解释:
在分发(dispatch)过程中,ViewSet
具有以下可用的属性:
basename
:用于创建的 URL 名称的基础部分。action
:当前操作的名称(例如,list
、create
)。detail
:一个布尔值,指示当前操作是针对列表视图还是详细视图进行配置。suffix
:视图集类型的显示后缀,与detail
属性相关。name
:视图集的显示名称。此参数与suffix
相互排斥。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 = [IsAdminUser]
return [permission() for permission in permission_classes]
Marking extra actions for routing
以下是对关于标记额外路由操作的解释:
如果您有一些临时的方法应该是可路由的,您可以使用 @action
装饰器将它们标记为可路由。和常规操作一样,额外的操作可以针对单个对象或整个集合。要表明这一点,可以将 detail
参数设置为 True
或 False
。
例如,DefaultRouter
会相应地配置 URL 模式。如果将 detail=True
,那么配置的详细操作的 URL 模式中将包含对象的主键(pk
)。
以下是一个示例:
from rest_framework import viewsets
from rest_framework.decorators import action
class UserViewSet(viewsets.ModelViewSet):
# 常规的视图集设置...
@action(detail=True, methods=['get'])
def custom_detail_action(self, request, pk=None):
# 处理针对单个用户的自定义操作的逻辑
return Response({'message': f'Custom detail action for user {pk}'})
@action(detail=False, methods=['get'])
def custom_collection_action(self, request):
# 处理针对用户集合的自定义操作的逻辑
return Response({'message': 'Custom collection action'})
在上述示例中,custom_detail_action
是一个针对单个用户(通过主键 pk
识别)的自定义操作,而 custom_collection_action
是针对用户集合的操作。
默认情况下,装饰器将路由请求,但也可以通过设置参数来接受其他 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):
...
然后,这两个新操作将在 urls 和 中可用。使用 and 参数更改操作的 URL 段和反向 URL 名称。^users/{pk}/set_password/$``^users/{pk}/unset_password/$``url_path``url_name
若要查看所有额外的操作,请调用该方法。.get_extra_actions()
3.GenericViewSet
GenericViewSet
是 Django REST framework 中的一个类,它继承自 ViewSetMixin
和 GenericAPIView
。
GenericViewSet
本身并不提供任何默认的操作实现,但是结合 mixins
类和使用 @action
装饰器,可以方便地构建出各种自定义的视图行为。
它的主要优势在于:
- 继承了
GenericAPIView
的常见属性和方法,如queryset
、serializer_class
等,方便进行数据获取和序列化的配置。 - 可以灵活地组合不同的
mixins
类(如CreateModelMixin
、RetrieveModelMixin
等)来实现具体的视图操作(如创建、获取、更新等)。 - 通过
@action
装饰器能够添加自定义的额外操作,并且可以精确控制这些操作的细节,比如请求方法、是否针对单个对象还是对象集合等。
以下是一个使用 GenericViewSet
构建 API 的示例:
首先,创建模型、序列化器和视图:
# models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=100)
publication_year = models.IntegerField()
# serializers.py
from rest_framework import serializers
from.models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
# views.py
from rest_framework import viewsets
from rest_framework.mixins import (
CreateModelMixin,
RetrieveModelMixin,
UpdateModelMixin,
DestroyModelMixin,
ListModelMixin
)
from.serializers import BookSerializer
from.models import Book
class BookViewSet(viewsets.GenericViewSet,
CreateModelMixin,
RetrieveModelMixin,
UpdateModelMixin,
DestroyModelMixin,
ListModelMixin):
queryset = Book.objects.all()
serializer_class = BookSerializer
然后,设置路由:
# urls.py
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from.views import BookViewSet
router = DefaultRouter()
router.register(r'books', BookViewSet)
urlpatterns = [
path('', include(router.urls)),
]
这样,就可以通过相应的 URL 对书籍数据进行创建、获取、更新、删除和获取列表等操作。
4.ModelViewSet
ModelViewSet
是 Django REST framework 中一个非常强大和方便的类,它继承自 GenericViewSet
,并结合了多个 Mixin
类(CreateModelMixin
、RetrieveModelMixin
、UpdateModelMixin
、DestroyModelMixin
、ListModelMixin
),从而提供了对模型数据的完整的 CRUD 操作(创建、读取、更新、删除和列表获取)的支持。
使用 ModelViewSet
的一般步骤如下:
- 定义模型、序列化器和
ModelViewSet
类:
from django.db import models
from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
# 模型
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=100)
publication_year = models.IntegerField()
# 序列化器
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
# ModelViewSet
class BookViewSet(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
- 设置路由:
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from.views import BookViewSet
router = DefaultRouter()
router.register(r'books', BookViewSet)
urlpatterns = [
path('', include(router.urls)),
]
这样,就可以通过发送相应的 HTTP 请求(如 GET
用于获取列表或单个对象,POST
用于创建,PUT
或 PATCH
用于更新,DELETE
用于删除)来操作书籍数据。
例如,使用 GET
请求 http://your_domain/books/
可以获取书籍列表,使用 GET
请求 http://your_domain/books/1/
可以获取 ID 为 1 的书籍详情,使用 POST
请求 http://your_domain/books/
并提供相应的数据可以创建新的书籍,等等。
关于权限设置
要在 ModelViewSet
中设置权限,可以通过以下几种方式:
- 在视图类中直接设置权限类:
from rest_framework.permissions import IsAuthenticated
from rest_framework.viewsets import ModelViewSet
class BookViewSet(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
permission_classes = [IsAuthenticated]
在上述示例中,使用了 IsAuthenticated
权限类,这意味着只有经过身份验证的用户才能访问该视图的操作。
- 自定义权限类并在视图中使用:
首先创建自定义的权限类:
from rest_framework import permissions
class IsAdminOrReadOnly(permissions.BasePermission):
def has_permission(self, request, view):
if request.method in permissions.SAFE_METHODS:
return True
return request.user.is_staff
然后在视图中应用:
class BookViewSet(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
permission_classes = [IsAdminOrReadOnly]
- 基于不同的操作设置不同的权限:
class BookViewSet(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
def get_permissions(self):
if self.action == 'create':
permission_classes = [IsAuthenticated]
elif self.action == 'update':
permission_classes = [IsAdminUser]
else:
permission_classes = [AllowAny]
return [permission() for permission in permission_classes]
上述示例中,根据不同的操作(create
、update
等)设置了不同的权限类。
Django REST framework 中除了 IsAuthenticated
,还有以下一些常用的权限类:
-
IsAdminUser
:只允许管理员用户访问。管理员用户通常是指is_staff=True
的用户。 -
AllowAny
:允许任何用户访问,不进行任何权限检查。 -
IsAuthenticatedOrReadOnly
:已认证的用户可以进行读写操作,未认证的用户只能进行只读(如GET
请求)操作。 -
DjangoModelPermissions
:检查用户是否具有与 Django 模型权限相关的权限。这通常与 Django 的默认权限系统配合使用,需要在模型的Meta
中定义权限。 -
DjangoObjectPermissions
:类似于DjangoModelPermissions
,但作用于对象级别,而不仅仅是模型级别。 -
TokenHasReadWriteScope
:用于验证 OAuth2 令牌是否具有读写权限。 -
ReadOnly
:只允许只读操作(如GET
、HEAD
和OPTIONS
请求)。 -
SAFE_METHODS
:这不是一个权限类,但常用于权限判断,表示GET
、HEAD
和OPTIONS
等安全方法。
可以根据具体的需求选择和组合使用这些权限类来控制对 API 的访问。
5.ReadOnlyModelViewSet
ReadOnlyModelViewSet
是 Django REST framework 中的一个视图集类。
它继承自 ModelViewSet
,但只允许只读操作,即只支持 GET
方法来获取列表(list
)和单个对象详情(retrieve
),不支持创建(create
)、更新(update
)和删除(destroy
)操作。
这对于只需要提供数据读取访问而不允许修改数据的场景非常有用,例如展示一些公共的信息、只读的资源等。
使用 ReadOnlyModelViewSet
与使用其他视图集类类似,您需要指定 queryset
和 serializer_class
等属性。
示例:
from rest_framework.viewsets import ReadOnlyModelViewSet
from your_app.models import YourModel
from your_app.serializers import YourModelSerializer
class YourReadOnlyModelViewSet(ReadOnlyModelViewSet):
queryset = YourModel.objects.all()
serializer_class = YourModelSerializer
ViewSet
和 GenericAPIView
ViewSet
和 GenericAPIView
有以下一些区别:
-
功能完整性:
GenericAPIView
提供了一些基础的属性和方法,如queryset
、serializer_class
等,以及一些用于获取数据和对象的基本方法,但不直接包含具体的请求处理方法(如get
、post
等)。ViewSet
则更像是一组相关操作的集合,它默认提供了常见的操作方法(如list
、create
、retrieve
、update
、destroy
),但这些方法在未通过as_view
方法绑定之前是未实现的。
-
处理请求方式:
GenericAPIView
需要开发者自己重写具体的请求处理方法(如get
、post
等)来实现相应的功能。ViewSet
中的操作方法是通过as_view
方法在最终确定视图时与具体的请求方法进行绑定。
-
路由配置:
- 对于
GenericAPIView
,通常需要手动在 URL 配置中为每个请求方法明确指定对应的路由。 ViewSet
更常与路由器(如DefaultRouter
)配合使用,路由器会根据ViewSet
中定义的操作自动生成相应的路由。
- 对于
-
代码复用性:
ViewSet
更适合处理一组相关且逻辑紧密的视图操作,有利于提高代码的复用性和一致性。GenericAPIView
则更灵活,适用于需要更精细自定义的场景。
-
复杂程度:
GenericAPIView
相对较简单,更易于理解和直接控制每个请求的处理逻辑。ViewSet
由于其集成度较高,对于初学者来说可能在理解和使用上稍微复杂一些。
在开发中选择 ViewSet
还是 GenericAPIView
可以考虑以下几个因素:
-
功能需求的复杂性:
- 如果您的视图逻辑相对简单,只需要处理基本的 CRUD 操作,并且希望利用框架提供的默认行为和自动路由生成,
ViewSet
可能是一个较好的选择。 - 如果您的视图需要更复杂的定制逻辑,例如根据不同的条件执行不同的操作,或者需要对请求和响应进行更精细的控制,
GenericAPIView
可能更合适,因为您可以更灵活地重写具体的方法。
- 如果您的视图逻辑相对简单,只需要处理基本的 CRUD 操作,并且希望利用框架提供的默认行为和自动路由生成,
-
代码复用性和一致性:
- 如果您有一组相关的视图操作,它们共享很多相同的属性和逻辑,
ViewSet
可以更好地封装这些共同部分,提高代码的复用性和一致性。 GenericAPIView
在处理多个不同但又有些相似的视图时,可能需要更多的重复代码设置。
- 如果您有一组相关的视图操作,它们共享很多相同的属性和逻辑,
-
开发效率:
- 对于快速开发和原型设计,
ViewSet
结合路由器可以快速搭建起基本的 API 结构。 - 如果您对 Django REST framework 的细节和底层原理有更深入的理解,并且需要更精确地控制视图的行为,使用
GenericAPIView
可能不会花费太多额外的时间,同时能满足特定需求。
- 对于快速开发和原型设计,
-
团队技术水平和项目规范:
- 如果团队成员对
ViewSet
的概念和使用比较熟悉,或者项目中有明确的规范要求使用某种方式,那么应遵循相应的规范。
- 如果团队成员对
-
未来的可扩展性:
- 考虑项目未来可能的功能扩展和变更。如果预计会有较多的定制和扩展需求,
GenericAPIView
可能提供了更大的灵活性。
- 考虑项目未来可能的功能扩展和变更。如果预计会有较多的定制和扩展需求,
总之,没有绝对的规则说一定要使用哪一个,需要根据具体的项目需求、团队情况和个人偏好来决定。