Django 官方推荐的姿势:类视图
wptr33 2025-05-23 20:38 6 浏览
作者:HelloGitHub-追梦人物
在开发网站的过程中,有一些视图函数虽然处理的对象不同,但是其大致的代码逻辑是一样的。比如一个博客和一个论坛,通常其首页都是展示一系列的文章列表或者帖子列表。对处理首页的视图函数来说,虽然其处理的对象一个是文章,另一个是帖子,但是其处理的过程是非常类似的:首先是从数据库取出文章或者帖子列表,然后将这些数据传递给模板并渲染模板。于是,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 官方文档对类视图的讲解,尽管我觉得这部分文档对类视图也讲得不是很清楚,不过也值得作为参考吧 基于类的视图概述[3]。
文章详情的类视图也写好了,同样的,你需要在 urls.py 中进行配置,将原来的函数视图 detail 改为类视图,相信你应该已经知道如何做了。
配置好详情页视图之后,访问一下文章的详情,可以看到页面返回的结果和函数视图是一模一样的,至此,类视图就改造完毕。因为类视图和函数视图是完全等价的,而且类视图具有代码复用等很多好处,所以以后一旦涉及视图,我们都会使用类视图来实现。
参考资料
[1]HelloGitHub-追梦人物: https://www.zmrenwu.com
[2]HelloGitHub-Team 仓库:
https://github.com/HelloGitHub-Team/HelloDjango-blog-tutorial
[3]基于类的视图概述:
https://docs.djangoproject.com/en/2.2/topics/class-based-views/
『讲解开源项目系列』——让对开源项目感兴趣的人不再畏惧、让开源项目的发起者不再孤单。跟着我们的文章,你会发现编程的乐趣、使用和发现参与开源项目如此简单。欢迎留言联系我们、加入我们,让更多人爱上开源、贡献开源~
相关推荐
- 每天一个AI姬,AMD核显用户有福了,AI绘画打破 NVIDIA 显卡垄断
-
使用StableDiffusion进行AI绘画,并不一定只能使用NVIDIA英伟达显卡,甚至,也不一定只能使用独立显卡。今天我们使用AMD6800H核显,并安装了StableDif...
- NETworkManager:功能强大的网络管理与问题排除工具
-
关于NETworkManagerNETworkManager是一款功能强大的网络管理与问题排除工具,该工具完全开源,可以帮助广大研究人员轻松管理目标网络系统并排除网络疑难问题。该工具使用远程桌面、Po...
- AMD也能深度学习+免费AI绘画:StableDiffusion+ROCm部署教程!
-
某国政客扇扇嘴皮子,CN玩硬件和深度学习的圈子里就掀起了一场风暴,这就是著名的嘴皮子效应(误)。没了高性能计算的A100H100倒也能理解,但是美利坚这波把RTX4090禁售了就让人无语了,所以不少做...
- windows 下编译 python_rtmpstream
-
最近在研究数字人,看了大咖的项目(https://github.com/lipku/metahuman-stream),尝试编译此项目的依赖项目python_rtmpstream(https://gi...
- 如何使用 Python 操作 Git 代码?GitPython 入门介绍
-
花下猫语:今天,我在查阅如何用Python操作Gitlab的时候,看到这篇文章,觉得还不错,特分享给大家。文中还提到了其它几种操作Git的方法,后续有机会的话,再陆续分享之~~作者:匿蟒...
- 网上看了不少,终于把ZlmediaKit流媒体框架搭建起来啦
-
你都站在2023年代了,视频通话、视频直播、视频会议、视频监控就是风口浪尖上的猪师兄,只要你学那么一丁点,拿个高薪的工作不过分吧!我也是半瓶子晃荡的,所以路人呀,共学习,同进步!本篇开始,只讲在Lin...
- MacDown:一款 macOS 的强大 Markdown 编辑器
-
大家好,很高兴又见面了,我是"...
- ZLMediaKit安装配置和推拉流
-
一、ZLMediaKit库简介ZLMediaKit是一个基于...
- 大神赞过的:学习 WebAssembly 汇编语言程序设计
-
文/阿里淘系F(x)Team-旭伦随着前端页面变得越来越复杂,javascript的性能问题一再被诟病。而Javascript设计时就不是为了性能优化设计的,这使得浏览器上可以运行的本地语言一...
- 【Docker】部署WVP视频监控平台
-
回来Docker系列,今天将会跟大家分享一则关于开源WVP视频监控平台的搭建。先说结论吧,一开始按照网上说的一步一步搭建没有搭建成功,不知道是版本太旧还是我这边机器有问题,尝试了好几个不同方式的搭建都...
- MongoDB+GridFS存储文件方案
-
GridFS是MongoDB的一个内置功能,它提供一组文件操作的API以利用MongoDB存储文件,GridFS的基本原理是将文件保存在两个Collection中,一个保存文件索引,一个保存文...
- 【开源】强大、创新且直观的 EDA套件
-
今天分享的LibrePCB是...
- Ollama如何制作自己的大模型?
-
背景Llama3发布了,这次用了...
- Ollama使用指南【超全版】
-
一、Ollama快速入门Ollama是一个用于在本地运行大型语言模型的工具,下面将介绍如何在不同操作系统上安装和使用Ollama。官网:https://ollama.comGithub:http...
- 基于区块链的价值共享互联网即时通讯应用平台源码免费分享
-
——————关注转发之后私信回复【源码】即可免费获取到本项目所有源码基于区块链的价值共享互联网即时通讯应用平台,是一个去中心化的任何人都可以使用的通讯网络,是一款基于区块链的价值共享互联网即时通讯AP...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
因果推断Matching方式实现代码 因果推断模型
-
git pull命令使用实例 git pull--rebase
-
git 执行pull错误如何撤销 git pull fail
-
面试官:git pull是哪两个指令的组合?
-
git fetch 和git pull 的异同 git中fetch和pull的区别
-
git pull 和git fetch 命令分别有什么作用?二者有什么区别?
-
还可以这样玩?Git基本原理及各种骚操作,涨知识了
-
git pull 之后本地代码被覆盖 解决方案
-
git命令之pull git.pull
-
- 最近发表
-
- 每天一个AI姬,AMD核显用户有福了,AI绘画打破 NVIDIA 显卡垄断
- NETworkManager:功能强大的网络管理与问题排除工具
- AMD也能深度学习+免费AI绘画:StableDiffusion+ROCm部署教程!
- windows 下编译 python_rtmpstream
- 如何使用 Python 操作 Git 代码?GitPython 入门介绍
- 网上看了不少,终于把ZlmediaKit流媒体框架搭建起来啦
- MacDown:一款 macOS 的强大 Markdown 编辑器
- ZLMediaKit安装配置和推拉流
- 大神赞过的:学习 WebAssembly 汇编语言程序设计
- 【Docker】部署WVP视频监控平台
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)