Django 官方推荐的姿势:类视图
文中所涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库
在开发网站的过程中,有一些视图函数虽然处理的对象不同,但是其大致的代码逻辑是一样的。比如一个博客和一个论坛,通常其首页都是展示一系列的文章列表或者帖子列表。对处理首页的视图函数来说,虽然其处理的对象一个是文章,另一个是帖子,但是其处理的过程是非常类似的:首先是从数据库取出文章或者帖子列表,然后将这些数据传递给模板并渲染模板。于是,django 把这些相同的逻辑代码抽取了出来,写成了一系列的通用视图函数,即基于类的通用视图(Generic Class Based View)。
使用类视图是 django 推荐的做法,熟悉了类视图的使用方法后,能够减少视图函数的重复代码,节省开发时间。接下来就让我们把博客应用中的视图函数改成基于类的通用视图。
ListView
在我们的博客应用中,有几个视图函数是从数据库中获取文章(Post)列表数据的:
blog/views.py
def index(request):
# ...
def archive(request, year, month):
# ...
def category(request, pk):
# ...
def tag(request, pk):
# ...
这些视图函数都是从数据库中获取文章(Post)列表,唯一的区别就是获取的文章列表可能不同。比如 index
获取全部文章列表,category
获取某个分类下的文章列表。
将 index 视图函数改写为类视图
针对这种从数据库中获取某个模型列表数据(比如这里的 Post 列表)的视图,Django 专门提供了一个 ListView
类视图。下面我们通过一个例子来看看 ListView
的使用方法。我们首先把 index
视图函数改造成类视图函数。
blog/views.py
from django.views.generic import ListView
class IndexView(ListView):
model = Post
template_name = 'blog/index.html'
context_object_name = 'post_list'
要写一个类视图,首先需要继承 django 提供的某个类视图。至于继承哪个类视图,需要根据你的视图功能而定。比如这里 IndexView
的功能是从数据库中获取文章(Post)列表,ListView
就是从数据库中获取某个模型列表数据的,所以 IndexView
继承 ListView
。
然后就是通过一些属性来指定这个视图函数需要做的事情,这里我们指定了三个属性:
- model:将 model 指定为
Post
,告诉 django 我要获取的模型是Post
。 - template_name:指定这个视图渲染的模板。
- context_object_name:指定获取的模型列表数据保存的变量名,这个变量会被传递给模板。
如果还是有点难以理解,不妨将类视图的代码和 index
视图函数的代码对比一下:
blog/views.py
def index(request):
post_list = Post.objects.all()
return render(request, 'blog/index.html', context={'post_list': post_list})
index
视图函数首先通过 Post.objects.all()
从数据库中获取文章(Post)列表数据,并将其保存到 post_list
变量中。而在类视图中这个过程 ListView
已经帮我们做了。我们只需告诉 ListView
去数据库获取的模型是 Post
,而不是 Comment
或者其它什么模型,即指定 model = Post
。将获得的模型数据列表保存到 post_list
里,即指定 context_object_name = 'post_list'
。然后渲染 blog/index.html 模板文件,index
视图函数中使用 render
函数。但这个过程 ListView
已经帮我们做了,我们只需指定渲染哪个模板即可。
接下来就是要将类视图转换成函数视图。为什么需要将类视图转换成函数视图呢?
我们来看一看 blog 的 URL 配置:
blog/urls.py
app_name = 'blog'
urlpatterns = [
path('', views.index, name='index'),
...
]
前面已经说过每一个 URL 对应着一个视图函数,这样当用户访问这个 URL 时,Django 就知道调用哪个视图函数去处理这个请求了。在 Django 中 URL 模式的配置方式就是通过 url
函数将 URL 和视图函数绑定。比如 path('', views.index, name='index')
,它的第一个参数是 URL 模式,第二个参数是视图函数 index
。对 url
函数来说,第二个参数传入的值必须是一个函数。而 IndexView
是一个类,不能直接替代 index
函数。好在将类视图转换成函数视图非常简单,只需调用类视图的 as_view()
方法即可(至于 as_view
方法究竟是如何将一个类转换成一个函数的目前不必关心,只需要在配置 URL 模式是调用 as_view
方法就可以了。具体的实现我们以后会专门开辟一个专栏分析类视图的源代码,到时候就能看出 django 使用的魔法了)。
现在在 URL 配置中把 index
视图替换成类视图 IndexView
:
blog/urls.py
app_name = 'blog'
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
...
]
访问一下首页,可以看到首页依然显示全部文章列表,和使用视图函数 index
时效果一模一样。
将 category 视图函数改写为类视图
category
视图函数的功能也是从数据库中获取文章列表数据,不过其和 index
视图函数不同的是,它获取的是某个分类下的全部文章。因此 category
视图函数中多了一步,即首先需要根据从 URL 中捕获的分类 id 并从数据库获取分类,然后使用 filter
函数过滤出该分类下的全部文章。来看看这种情况下类视图该怎么写:
blog/views.py
class CategoryView(ListView):
model = Post
template_name = 'blog/index.html'
context_object_name = 'post_list'
def get_queryset(self):
cate = get_object_or_404(Category, pk=self.kwargs.get('pk'))
return super(CategoryView, self).get_queryset().filter(category=cate)
和 IndexView
不同的地方是,我们覆写了父类的 get_queryset
方法。该方法默认获取指定模型的全部列表数据。为了获取指定分类下的文章列表数据,我们覆写该方法,改变它的默认行为。
首先是需要根据从 URL 中捕获的分类 id(也就是 pk)获取分类,这和 category
视图函数中的过程是一样的。不过注意一点的是,在类视图中,从 URL 捕获的路径参数值保存在实例的 kwargs
属性(是一个字典)里,非路径参数值保存在实例的 args
属性(是一个列表)里。所以我们使了 self.kwargs.get('pk')
来获取从 URL 捕获的分类 id 值。然后我们调用父类的 get_queryset
方法获得全部文章列表,紧接着就对返回的结果调用了 filter
方法来筛选该分类下的全部文章并返回。
此外我们可以看到 CategoryView
类中指定的属性值和 IndexView
中是一模一样的,所以如果为了进一步节省代码,甚至可以直接继承 IndexView
:
class CategoryView(IndexView):
def get_queryset(self):
cate = get_object_or_404(Category, pk=self.kwargs.get('pk'))
return super(CategoryView, self).get_queryset().filter(category=cate)
然后就在 URL 配置中把 category
视图替换成类视图 CategoryView
:
blog/urls.py
app_name = 'blog'
urlpatterns = [
...
path('categories/<int:pk>/', views.CategoryView.as_view(), name='category'),
]
访问以下某个分类页面,可以看到依然显示的是该分类下的全部文章列表,和使用视图函数 category
时效果一模一样。
将 archive 和 tag 视图函数改写成类视图
这里没有什么新东西要讲了,学以致用,这个任务就交给你自己了。
DetailView
除了从数据库中获取模型列表的数据外,从数据库获取模型的一条记录数据也是常见的需求。比如查看某篇文章的详情,就是从数据库中获取这篇文章的记录然后渲染模板。对于这种类型的需求,django 提供了一个 DetailView
类视图。下面我们就来将 detail
视图函数转换为等价的类视图 PostDetailView
,代码如下:
blog/views.py
from django.views.generic import ListView, DetailView
# 记得在顶部导入 DetailView
class PostDetailView(DetailView):
# 这些属性的含义和 ListView 是一样的
model = Post
template_name = 'blog/detail.html'
context_object_name = 'post'
def get(self, request, *args, **kwargs):
# 覆写 get 方法的目的是因为每当文章被访问一次,就得将文章阅读量 +1
# get 方法返回的是一个 HttpResponse 实例
# 之所以需要先调用父类的 get 方法,是因为只有当 get 方法被调用后,
# 才有 self.object 属性,其值为 Post 模型实例,即被访问的文章 post
response = super(PostDetailView, self).get(request, *args, **kwargs)
# 将文章阅读量 +1
# 注意 self.object 的值就是被访问的文章 post
self.object.increase_views()
# 视图必须返回一个 HttpResponse 对象
return response
def get_object(self, queryset=None):
# 覆写 get_object 方法的目的是因为需要对 post 的 body 值进行渲染
post = super().get_object(queryset=None)
md = markdown.Markdown(extensions=[
'markdown.extensions.extra',
'markdown.extensions.codehilite',
# 记得在顶部引入 TocExtension 和 slugify
TocExtension(slugify=slugify),
])
post.body = md.convert(post.body)
m = re.search(r'<div class="toc">\s*<ul>(.*)</ul>\s*</div>', md.toc, re.S)
post.toc = m.group(1) if m is not None else ''
return post
PostDetailView
稍微复杂一点,主要是等价的 detail
视图函数本来就比较复杂,下面来一步步对照 detail
视图函数中的代码讲解。
首先我们为 PostDetailView
类指定了一些属性的值,这些属性的含义和 ListView
中是一样的,这里不再重复讲解。
紧接着我们覆写了 get
方法。这对应着 detail
视图函数中将 post 的阅读量 +1 的那部分代码。事实上,你可以简单地把 get
方法的调用看成是 detail
视图函数的调用。
接着我们又复写了 get_object
方法。这对应着 detail
视图函数中根据文章的 id(也就是 pk)获取文章,然后对文章的 post.body 进行 Markdown 解析的代码部分。
你也许会被这么多方法搞乱,为了便于理解,你可以简单地把 get
方法看成是 detail
视图函数,至于其它的像 get_object
、get_context_data
都是辅助方法,这些方法最终在 get
方法中被调用,这里你没有看到被调用的原因是它们隐含在了 super(PostDetailView, self).get(request, *args, **kwargs)
即父类 get
方法的调用中。最终传递给浏览器的 HTTP 响应就是 get
方法返回的 HttpResponse
对象。
还是无法理解么?在不涉及源码的情况下我也只能讲这么多了。要想熟练掌握并灵活运用类视图必须仔细阅读类视图的源码,我当时也是啃源码啃了很久很久,以后我会专门开辟一个专题分析类视图的源码,到时候你就会对类视图有更深的理解了。此外,这里是 django 官方文档对类视图的讲解,尽管我觉得这部分文档对类视图也讲得不是很清楚,不过也值得作为参考吧 基于类的视图概述。
文章详情的类视图也写好了,同样的,你需要在 urls.py 中进行配置,将原来的函数视图 detail
改为类视图,相信你应该已经知道如何做了。
配置好详情页视图之后,访问一下文章的详情,可以看到页面返回的结果和函数视图是一模一样的,至此,类视图就改造完毕。因为类视图和函数视图是完全等价的,而且类视图具有代码复用等很多好处,所以以后一旦涉及视图,我们都会使用类视图来实现。
『讲解开源项目系列』——让对开源项目感兴趣的人不再畏惧、让开源项目的发起者不再孤单。跟着我们的文章,你会发现编程的乐趣、使用和发现参与开源项目如此简单。欢迎留言联系我们、加入我们,让更多人爱上开源、贡献开源~
Django 官方推荐的姿势:类视图的更多相关文章
- Django内置的通用类视图
1.ListView 表示对象列表的一个页面. 执行这个视图的时候,self.object_list将包含视图正在操作的对象列表(通常是一个查询集,但不是必须). 属性: model: 指定模型 te ...
- Django框架的使用教程--类视图-中间间-模板[六]
类视图 类视图的使用 视图函数 class class_view(View): """类视图""" def get(self, reques ...
- 六、Django之表单和类视图-Part 4
一.表单form 为了接收用户的投票选择,我们需要在前端页面显示一个投票界面.让我们重写先前的polls/detail.html文件,代码如下: <h1>{{ question.quest ...
- Django:(05)类视图,装饰器和中间件
一.类视图的定义和使用 在Django中还可以通过类来定义一个视图,称为类视图. 定义一个类视图:定义一个类,需继承 Django 提供的 View 类 . from django.views.gen ...
- Django ListView DetailView等基于类的视图如何添加装饰器?
场景: Django开发中,如果我们使用了类视图,如:ListView.DetailView.UpdateView等,这时我们又想要对这个视图添加一个装饰器,来实现某种功能,这时候该怎么处理呢? 环境 ...
- 在DJANGO的类视图中实现登陆要求和权限保护
以前接触的是基于函数的保护,网上材料比较多. 但基于类视图的很少. 补上! Decorating class-based views 装饰类视图 对于类视图的扩展并不局限于使用mixin.你也可以使用 ...
- Part 4:表单和类视图--Django从入门到精通系列教程
该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. Python及Django学习QQ群:453 ...
- Django学习笔记之视图高级-类视图
类视图 在写视图的时候,Django除了使用函数作为视图,也可以使用类作为视图.使用类视图可以使用类的一些特性,比如继承等. View django.views.generic.base.View是主 ...
- Django 类视图
引文 所有的类视图都继承django.views.generic.base.View类. 在URLconf中简单的使用通用视图 如果只是简单的做一些属性修改,可以使用as_view()方法,如下所示: ...
随机推荐
- 降低 80% 的读写响应延迟!我们测评了 etcd 3.4 新特性(内含读写发展史)
作者 | 陈洁(墨封) 阿里云开发工程师 导读:etcd 作为 K8s 集群中的存储组件,读写性能方面会受到很多压力,而 etcd 3.4 中的新特性将有效缓解压力,本文将从 etcd 数据读写机制 ...
- PHP 扩展开发初探
什么是 PHP 扩展 通俗说,PHP 扩展是增强 PHP 语言功能的插件.PHP 提供了编程语言的语法,比如分支.循环.函数.类等,这些是 PHP 本身所提供的.在某些情况下需要在 PHP 语言的基础 ...
- maven仓库 - nexus配置
搭建环境: 腾讯云服务器 CentOS 6.8.jdk7.sonatype nexus.maven.Xshell 5 版本信息: jdk : jdk-7u80-linux-x64.tar.gz nex ...
- git分支操作笔记
git常用的基本操作 远程仓库只有一个master分支,创建dev分支并上传 # 创建本地dev分支 git checkout -b dev master # 推送dev分支到远程仓库 git pus ...
- 从零开始入门 K8s | 应用配置管理
一.需求来源 背景问题 首先一起来看一下需求来源.大家应该都有过这样的经验,就是用一个容器镜像来启动一个 container.要启动这个容器,其实有很多需要配套的问题待解决: 第一,比如说一些可变的配 ...
- 推荐5款自学手机APP,请低调收藏,让你变得越来越优秀
现在的手机APP真的是太多了,但里面的功能同类性又非常大,很难找到实用并且符合要求的APP.接下来就为小伙伴们推荐5款非常实用的APP软件,保证你会爱不释手,轻松秒变手机达人. 1.清爽视频编辑器 一 ...
- Spring boot - 梳理 - 根本上说,Spring Boot项目只不过是一个普通的Spring项目,只是使用了Spring Boot的起步依赖和自动配置
根本上说,Spring Boot项目只不过是一个普通的Spring项目,只是使用了Spring Boot的起步依赖和自动配置
- 基于SpringBoot + Mybatis实现 MVC 项目
1.预览: (1)完整项目结构 (2) 创建数据库.数据表: [user.sql] SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- ...
- Java初中级面试笔记及对应视频讲解
笔试题链接:点击打开链接 密码:提取码:7h9e 视频下载链接: 点击打开链接 提取码:hyye 百万it课程 https://pan.baidu.com/s/1ldJ_Ak7y0VL5Xmy9 ...
- Flask基础(03)-->创建第一个Flask程序
# 导入Flask from flask import Flask # 创建Flask的应用程序 # 参数__name__指的是Flask所对应的模块,其决定静态文件从哪个地方开始寻找 app = F ...