刚开始写views.py模块的代码,一般都是用def定义的函数视图,不过DRF更推荐使用class定义的类视图,这能让我们的代码更符合DRY(Don't Repeat Yourself)设计原则:

使用APIView

rest_framework.views.APIView是DRF封装的API视图,继承了django.views.generic.base.View

我们用它把函数视图改写成类视图,编辑snippets/views.py

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status class SnippetList(APIView):
"""
List all snippets, or create a new snippet.
"""
def get(self, request, format=None):
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return Response(serializer.data) def post(self, request, format=None):
serializer = SnippetSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) class SnippetDetail(APIView):
"""
Retrieve, update or delete a snippet instance.
"""
def get_object(self, pk):
try:
return Snippet.objects.get(pk=pk)
except Snippet.DoesNotExist:
raise Http404 def get(self, request, pk, format=None):
snippet = self.get_object(pk)
serializer = SnippetSerializer(snippet)
return Response(serializer.data) def put(self, request, pk, format=None):
snippet = self.get_object(pk)
serializer = SnippetSerializer(snippet, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def delete(self, request, pk, format=None):
snippet = self.get_object(pk)
snippet.delete()
return Response(status=status.HTTP_204_NO_CONTENT)

类视图的代码跟函数视图是非常类似的,区别在于GETPOST等方法是用的函数而不是if语句,可以更好的解耦代码。

改了views.py代码后,需要同时修改snippets/urls.py

from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views urlpatterns = [
path('snippets/', views.SnippetList.as_view()),
path('snippets/<int:pk>/', views.SnippetDetail.as_view()),
] urlpatterns = format_suffix_patterns(urlpatterns)

为什么要加个as_view()方法?

因为path()的参数必须是可调用的,在源码中能看到elif callable(view)

def _path(route, view, kwargs=None, name=None, Pattern=None):
if isinstance(view, (list, tuple)):
# For include(...) processing.
pattern = Pattern(route, is_endpoint=False)
urlconf_module, app_name, namespace = view
return URLResolver(
pattern,
urlconf_module,
kwargs,
app_name=app_name,
namespace=namespace,
)
# callable判断
elif callable(view):
pattern = Pattern(route, name=name, is_endpoint=True)
return URLPattern(pattern, view, kwargs, name)
else:
raise TypeError('view must be a callable or a list/tuple in the case of include().')

as_view()方法返回了一个内部定义的可调用函数:

@classonlymethod
def as_view(cls, **initkwargs):
"""Main entry point for a request-response process."""
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError(
'The method name %s is not accepted as a keyword argument '
'to %s().' % (key, cls.__name__)
)
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key)) # 内部定义了可调用函数
def view(request, *args, **kwargs):
self = cls(**initkwargs)
self.setup(request, *args, **kwargs)
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs # take name and docstring from class
update_wrapper(view, cls, updated=()) # and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view

使用mixins

DRF提供了rest_framework.mixins模块,封装了类视图常用的增删改查方法:

比如新增CreateModelMixin

class CreateModelMixin:
"""
Create a model instance.
"""
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) def perform_create(self, serializer):
serializer.save() def get_success_headers(self, data):
try:
return {'Location': str(data[api_settings.URL_FIELD_NAME])}
except (TypeError, KeyError):
return {}

类视图继承了Mixin后,可以直接使用它的.create()方法,类似的还有.list().retrieve().update().destroy()。我们按照这个思路来简化snippets/views.py代码:

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import mixins
from rest_framework import generics class SnippetList(mixins.ListModelMixin,
mixins.CreateModelMixin,
generics.GenericAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs) class SnippetDetail(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs) def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)

瞬间少了好多代码,真够DRY的。

什么是mixin?

维基百科的解释:

In object-oriented programming languages, a mixin (or mix-in) is a class that contains methods for use by other classes without having to be the parent class of those other classes.

不太好理解。

换句话说,mixin类提供了一些方法,我们不会直接用这些方法,而是把它添加到其他类来使用。

还是有点抽象。

再简单点说,mixin只不过是实现多重继承的一个技巧而已。

这下应该清楚了。

使用generics

如果仔细看snippets/views.py的代码,就会发现我们用到了from rest_framework import generics

generics.GenericAPIView

这是DRF提供的通用API类视图,mixins只提供了处理方法,views.py中的类要成为视图,还需要继承GenericAPIViewGenericAPIView继承了本文第一小节提到的rest_framework.views.APIView。除了GenericAPIView,我们还可以用其他的类视图进一步简化代码:

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import generics class SnippetList(generics.ListCreateAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer

看看ListCreateAPIView的源码:

class ListCreateAPIView(mixins.ListModelMixin,
mixins.CreateModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset or creating a model instance.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)

真DRY!

东方说

学到这里,已经开始感受到了Django REST framework的强大之处了,我觉得学一个框架,不仅要看如何使用,还需要了解它的设计思路和底层实现,这样才能更好的总结为自己的编程思想,写出更漂亮的代码。

参考资料:

https://www.django-rest-framework.org/tutorial/3-class-based-views/#tutorial-3-class-based-views

https://stackoverflow.com/questions/533631/what-is-a-mixin-and-why-are-they-useful

https://www.zhihu.com/question/20778853

DRF类视图让你的代码DRY起来的更多相关文章

  1. Django RestFramework(DRF)类视图

    基础视图 1.基础函数视图(@api_view) DRF提供了一种函数基础视图来装饰Django的普通视图,我们可以使用request来接受请求和response响应.一个小例子: from rest ...

  2. Part 4:表单和类视图--Django从入门到精通系列教程

    该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. Python及Django学习QQ群:453 ...

  3. 六、Django之表单和类视图-Part 4

    一.表单form 为了接收用户的投票选择,我们需要在前端页面显示一个投票界面.让我们重写先前的polls/detail.html文件,代码如下: <h1>{{ question.quest ...

  4. 补充01 Django 类视图

    视图 函数视图[Function Base View] 以函数的方式定义的视图称为函数视图,函数视图便于理解.但是遇到一个视图对应的路径提供了多种不同HTTP请求方式的支持时,便需要在一个函数中编写不 ...

  5. django类视图的使用

    1 类视图引入 以函数的方式定义的视图称为函数视图,函数视图便于理解. 但是遇到一个视图对应的路径提供了多种不同HTTP请求方式的支持时,便需要在一个函数中编写不同的业务逻辑,代码可读性与复用性都不佳 ...

  6. drf之视图类与路由

    视图 Django REST framwork 提供的视图的主要作用: 控制序列化器的执行(检验.保存.转换数据) 控制数据库查询的执行 2个视图基类 APIView rest_framework.v ...

  7. 0014 基于DRF框架开发(02 基类视图 GenericAPIView)

    前端于对数据操作的请求基本上就分为四类:增删改查,即增加.删除.修改.查询. 而DRF把前端请求分为两个大类:带ID参数请求和不带ID参数请求. 不带ID参数请求包括:增加.分布多条查询 带ID参数请 ...

  8. DRF的视图

    DRF的视图 APIView 我们django中写CBV的时候继承的是View,rest_framework继承的是APIView,那么他们两个有什么不同呢~~~ urlpatterns = [    ...

  9. DRF之视图和router

    1. 视图 Django REST framwork 提供的视图的主要作用: 控制序列化器的执行(检验.保存.转换数据) 控制数据库查询的执行 1.1. 请求与响应 1.1.1 Request RES ...

随机推荐

  1. 下载器Folx扩展程序支持哪些浏览器

    Folx使用多线程的下载方式大大提升了下载的速度,可以完全替代浏览器自带的下载工具,使下载文件的管理更加简单高效.但是,必须给浏览器安装Folx扩展程序,才能使用Folx下载页面链接. Folx在偏好 ...

  2. Mac中的格式转换如何用读写工具Tuxera NTFS完成

    Tuxera NTFS for Mac是一款专门为Mac用户提供的NTFS驱动软件,它不仅可以进行磁盘文件的访问.编辑.传输和存储,还可以对硬盘进行维修检查以及修复. 今天小编就给大家简单介绍一下Tu ...

  3. kafka入门之broker--日志存储设计

    kafaka并不是直接将原省消息写入日志文件的,相反,它会将消息和一些必要的元数据信息大宝在一起封装成一个record写入日志.其实就是我们之前介绍的batch 具体对每个日志而言,kafka又将其进 ...

  4. Linux(Ubuntu16.04)下的MediaWiki的部署启动

    一.服务器部分 使用XAMPP配置MediaWiki部署所需要的环境. 1. 查看服务器的版本与位数: sudo lsb_release -a      //查看系统版本 uname -a       ...

  5. LeetCode 036 Valid Sudoku

    题目要求:Valid Sudoku Determine if a Sudoku is valid, according to: Sudoku Puzzles - The Rules. The Sudo ...

  6. FPGA 串口

    VerilogHDL那些事儿_建模篇(黑金FPGA开发板配套教程) 作者:akuei2 说明:参照该书将部分程序验证学习一遍 学习时间:2014年5月3号 主要收获: 1. 对串口有初步了解: 2. ...

  7. Beta冲刺随笔——Day_Two

    这个作业属于哪个课程 软件工程 (福州大学至诚学院 - 计算机工程系) 这个作业要求在哪里 Beta 冲刺 这个作业的目标 团队进行Beta冲刺 作业正文 正文 其他参考文献 无 今日事今日毕 林涛: ...

  8. 网络编程原理与UDP实现

    如何发送数据包? Q:当应用程序产生数据的时候,需要去构造数据包并发送到网络上去,但是由谁负责处理呢? A:现代操作系统负责数据包得构造与发送,应用程序只需提供数据. 当应用程序产生数据时,应用程序将 ...

  9. scentos7安装redis,以及redis的主从配置

    redis的安装 下载redis安装包 wget http://download.redis.io/releases/redis-4.0.6.tar.gz 解压压缩包 tar -zxvf redis- ...

  10. PyQt(Python+Qt)学习随笔:QMdiArea多文档界面部件的subWindowActivated信号

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 QMdiArea的subWindowActivated在一个窗口激活( ...