Django REST framework允许将一组相关视图的逻辑组合到一个称为ViewSet
的类中。
ViewSet
类只是一种基于类的View,它不提供任何处理方法,如.get()
或.post()
,而是提供诸如.list()
和.create()
之类的操作。
通常,不是在urlconf中的视图集中明确注册视图,而是使用路由器类注册视图集,这会自动确定urlconf。
使用ViewSet
类比使用View类有两个主要优点。
继承自APIView
,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。
ViewSet主要通过继承ViewSetMixin
来实现在调用as_view()
时传入字典的映射处理工作。
在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。
使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中。
ViewSet视图集类不再实现get()、post()等方法,而是实现动作action如list()、create()等。
ViewSet默认应该有如下几种动作:
如果自身逻辑上述action无法满足,可以通过自定义方法。
class BooksInfoAPIView(ViewSet):
def list(self, request):
books = BooksInfo.objects.all()
serializer = BookInfoModelSerializer(instance=books, many=True)
return Response(serializer.data)
def retrieve(self, request, pk):
try:
book = BooksInfo.objects.get(pk=pk)
except BooksInfo.DoesNotExist:
return Response('404 not found', status=HTTP_404_NOT_FOUND)
serializer = BookInfoModelSerializer(instance=book)
return Response(serializer.data)
def create(self, request):
data = request.data
serializer = BookInfoModelSerializer(data=data)
if not serializer.is_valid():
return Response(serializer.errors)
serializer.save()
return Response(serializer.validated_data)
def update(self, request, pk):
data = request.data
book = BooksInfo.objects.get(id=pk)
serializer = BookInfoModelSerializer(instance=book, data=data, partial=True)
if not serializer.is_valid():
return Response(serializer.errors)
serializer.save()
return Response(serializer.validated_data)
def destroy(self, request, pk):
book = BooksInfo.objects.get(id=pk)
book.delete()
return Response("删除成功")
在设置路由时,如下操作
urlpatterns = [
path('books/', BooksInfoAPIView.as_view({'get': 'list', 'post': 'create'}), name='all_book'),
path('books/<pk>/', BooksInfoAPIView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}),
name="book_detail"),
]
使用ViewSet通常并不方便,以为list、retrieve、create、update、destory等方法都需要自己编写。
GenericViewSet继承自GenericAPIView
与ViewSetMixin
,在实现了调用as_view()
时传入字典的映射处理工作的同时,还提供了GenericAPIView
提供的基础方法,可以直接搭配Mixin扩展类使用。
class BooksInfoAPIView(GenericViewSet):
queryset = BooksInfo.objects.all()
serializer_class = BookInfoModelSerializer
def list(self, request):
books = self.get_queryset()
serializer = self.get_serializer(instance=books, many=True)
return Response(serializer.data)
def retrieve(self, request, pk):
book = self.get_object()
serializer = self.get_serializer(instance=book)
return Response(serializer.data)
def create(self, request):
data = request.data
serializer = self.get_serializer(data=data)
if not serializer.is_valid():
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
serializer.save()
return Response(serializer.validated_data, status=HTTP_201_CREATED)
def update(self, request, pk):
data = request.data
book = self.get_object()
serializer = self.get_serializer(instance=book, data=data, partial=True)
if not serializer.is_valid():
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
serializer.save()
return Response(serializer.validated_data, status=HTTP_200_OK)
def destroy(self, request, pk):
book = self.get_object()
book.delete()
return Response("删除成功", status=HTTP_200_OK)
路由
urlpatterns = [
path('books/', BooksInfoAPIView.as_view({'get': 'list', 'post': 'create'}), name='all_book'),
path('books/<pk>/', BooksInfoAPIView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}),
name="book_detail"),
]
GenericViewSet还可以像GenericAPIView一样,搭配各种Mixins来实现增删改查功能
class BooksInfoAPIView(ListModelMixin, RetrieveModelMixin, CreateModelMixin,
UpdateModelMixin, DestroyModelMixin, GenericViewSet):
queryset = BooksInfo.objects.all()
serializer_class = BookInfoModelSerializer
class HeroInfoAPIView(ListModelMixin, RetrieveModelMixin, CreateModelMixin,
UpdateModelMixin, DestroyModelMixin, GenericViewSet):
queryset = HeroInfo.objects.all()
serializer_class = HeroInfoModelSerializer
继承自GenericAPIVIew
,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
class BooksInfoAPIView(ModelViewSet):
queryset = BooksInfo.objects.all()
serializer_class = BookInfoModelSerializer
class HeroInfoAPIView(ModelViewSet):
queryset = HeroInfo.objects.all()
serializer_class = HeroInfoModelSerializer
完成最基本的增删改查功能,当需求无法完成时,可以通过重写对应方法实现功能。
继承自GenericAPIVIew
,同时包括了ListModelMixin、RetrieveModelMixin
class BooksReadOnlyView(ReadOnlyModelViewSet):
queryset = BooksInfo.objects.all()
serializer_class = BookInfoModelSerializer
urls:
urlpatterns = [
path('book/', BooksReadOnlyView.as_view({'get': 'list'}), name='book'),
path('book/<pk>/', BooksReadOnlyView.as_view({'get': 'retrieve'}), name="bookDetail"),
]
最基本的增删改查使用ModelViewSet可以省略代码,但当业务逻辑提升,默认的增删改查已经无法完成工作时,需要新增额外的行为来完成接口的编写
class BooksInfoAPIView(ModelViewSet):
queryset = BooksInfo.objects.all()
serializer_class = BookInfoModelSerializer
# 查询最后一本书
def latest(self, request, *args, **kwargs):
book = BooksInfo.objects.latest('id')
print(book)
serializer = self.get_serializer(book)
print(serializer.data)
return Response(serializer.data)
# 新增阅读量
def read(self, request, pk, *args, **kwargs):
data = request.data
print(data)
book = self.get_object()
print(book)
serializer = self.get_serializer(instance=book, data=data, partial=True)
if not serializer.is_valid():
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
serializer.save()
return Response(serializer.data, status=HTTP_206_PARTIAL_CONTENT)
urls:
urlpatterns = [
path('books/', BooksInfoAPIView.as_view({'get': 'list', 'post': 'create'}), name='all_book'),
path('books/<int:pk>/', BooksInfoAPIView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}),
name="book_detail"),
path('books/latest/', BooksInfoAPIView.as_view({'get': 'latest'}), name="latest_book"),
path('books/<int:pk>/read/', BooksInfoAPIView.as_view({'put': 'read'}), name="update_read"),
]
在调度期间,当前action的名称可以通过.action
属性获得。可以通过检查.action
以根据当前action调整行为。
例如,可以将权限限制为只有admin才能访问list
以外的其他action:
def get_permissions(self):
if self.action == "list":
permission_classes = [IsAuthenticated]
else:
permission_classes = [IsAdmin]
return [permission() for permission in permission_classes]
如果需要路由特定方法,则可以用@detail_route
或@list_route
装饰器进行修饰。
@list_route
修饰器适用于在对象列表上操作的方法。@detail_route
装饰器在其URL模式中包含pk
,用于支持需要获取单个实例的方法。
如果需要获取action的URL,请使用.reverse_action()
方法。这是.reverse()
的一个便捷包装,它会自动传递视图的请求对象,并将url_name
与.basename
属性挂接。
通过对各个类源码的拜读,我只想说DRF的作者真的把类继承的用法发挥到了完美。
基于Nginx+Supervisord+uWSGI+Django1.11.1+Python3.6.5构建