此文档为官方文档的翻译,原文地址:https://www.django-rest-framework.org/tutorial/3-class-based-views/

我们也可以使用类视图来取代原始的函数视图。待会你会发现类视图原来如此强大,强大到你的代码简洁的跟Hello World一样。

用类视图重写API

  1. from snippets.models import Snippet
  2. from snippets.serializers import SnippetSerializer
  3. from django.http import Http404
  4. from rest_framework.views import APIView
  5. from rest_framework.response import Response
  6. from rest_framework import status
  7. class SnippetList(APIView):
  8. """
  9. 列出所有Snippet,或者创建一个新的Snippet
  10. """
  11. def get(self, request, format=None):
  12. snippets = Snippet.objects.all()
  13. serializer = SnippetSerializer(snippets, many=True) # 当序列化对象类型为QuerySet时,记得使用many这一关键字参数哦~
  14. return Response(serializer.data)
  15. def post(self, request, format=None):
  16. serializer = SnippetSerializer(data=request.data)
  17. if serializer.is_valid():
  18. serializer.save()
  19. return Response(serializer.data, status=status.HTTP_201_CREATED)
  20. return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

怎么样,这是不是很清爽,也很Django?

同样的操作,改一下实例视图:

  1. class SnippetDetail(APIView):
  2. """
  3. 检索、更新或删除一个Snippet实例。
  4. """
  5. def get_object(self, pk):
  6. try:
  7. return Snippet.objects.get(pk=pk)
  8. except Snippet.DoesNotExist:
  9. raise Http404
  10. def get(self, request, pk, format=None):
  11. snippet = self.get_object(pk)
  12. serializer = SnippetSerializer(snippet)
  13. return Response(serializer.data)
  14. def put(self, request, pk, format=None):
  15. snippet = self.get_object(pk)
  16. serializer = SnippetSerializer(snippet, data=request.data)
  17. if serializer.is_valid():
  18. serializer.save()
  19. return Response(serializer.data)
  20. return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  21. def delete(self, request, pk, format=None):
  22. snippet = self.get_object(pk)
  23. snippet.delete()
  24. return Response(status=status.HTTP_204_NO_CONTENT)

重构完了视图,也要重构url,像之前使用Django一样:

  1. from django.urls import path
  2. from rest_framework.urlpatterns import format_suffix_patterns
  3. from snippets import views
  4. urlpatterns = [
  5. path('snippets/', views.SnippetList.as_view()),
  6. path('snippets/<int:pk>/', views.SnippetDetail.as_view()),
  7. ]
  8. urlpatterns = format_suffix_patterns(urlpatterns)

OK,重构结束!如果你没出什么岔子,整个项目还能像之前一样运行。

修改完代码,结构上确实优雅了,不过,这工作量和之前也没减少多少呀……

别急,玩斗地主哪有一开始就放王炸的,耐心看下去~

使用 Mixin

使用基于类的视图的一大优点是:它允许我们轻松地组合可重用的行为

到目前为止,我们使用的create/retrieve/update/delete 操作对于我们创建的任何模型支持的 API 视图来说都非常相似。

DRF将这些常用的操作抽象提取畜类,这就成了DRF中的Mixin类。

我们再来重构一下视图:

  1. from snippets.models import Snippet
  2. from snippets.serializers import SnippetSerializer
  3. from rest_framework import mixins
  4. from rest_framework import generics
  5. class SnippetList(mixins.ListModelMixin,
  6. mixins.CreateModelMixin,
  7. generics.GenericAPIView):
  8. queryset = Snippet.objects.all() # 获取queryset
  9. serializer_class = SnippetSerializer # 获取序列化类
  10. def get(self, request, *args, **kwargs):
  11. return self.list(request, *args, **kwargs)
  12. def post(self, request, *args, **kwargs):
  13. return self.create(request, *args, **kwargs)

我们将花一点时间来检查这里到底发生了什么。我们正在使用 GenericAPIView 构建视图,并添加了 ListModelMixinCreateModelMixin

基类提供了核心功能,而mixin类提供了.list().create()操作。然后显式地getpost方法绑定到相应的操作。到目前为止,这些都很简单。

  1. class SnippetDetail(mixins.RetrieveModelMixin,
  2. mixins.UpdateModelMixin,
  3. mixins.DestroyModelMixin,
  4. generics.GenericAPIView):
  5. queryset = Snippet.objects.all()
  6. serializer_class = SnippetSerializer
  7. def get(self, request, *args, **kwargs):
  8. return self.retrieve(request, *args, **kwargs)
  9. def put(self, request, *args, **kwargs):
  10. return self.update(request, *args, **kwargs)
  11. def delete(self, request, *args, **kwargs):
  12. return self.destroy(request, *args, **kwargs)

类似的操作,我们再次使用GenericAPIView类来提供核心功能,并添加mixin来提供.retrieve().update().destroy()操作。

看到这里,你有没感到一丝丝惊喜的意味呢?

别急,Mixin还只是中级兵,下面来介绍高级兵——通用类视图。

使用通用类视图(优雅带师!)

使用mixin类重构视图后,我们的代码比以前稍微少了一些,但我们可以更进一步。
REST框架提供了一组混合的通用视图,我们可以使用它们来进一步精简views.py模块。

  1. from snippets.models import Snippet
  2. from snippets.serializers import SnippetSerializer
  3. from rest_framework import generics
  4. class SnippetList(generics.ListCreateAPIView):
  5. queryset = Snippet.objects.all()
  6. serializer_class = SnippetSerializer
  7. class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
  8. queryset = Snippet.objects.all()
  9. serializer_class = SnippetSerializer

Wow!惊不惊喜?开不开心?

50多行的代码简化到不到10行,终于体会到了那句话的精髓:

人生苦短,我用Python!