Django 的 MVT 架构
文章目录
-
- [Django 的 MVT 架构](#Django 的 MVT 架构)
- [01:Django ------ 那个"自带电池"的 Web 巨无霸](#01:Django —— 那个“自带电池”的 Web 巨无霸)
-
- [🧐 为什么是 Django?](#🧐 为什么是 Django?)
-
- [核心哲学:Batteries Included(自带电池)](#核心哲学:Batteries Included(自带电池))
- [🏗️ 架构拆解:MVT 是个啥?](#🏗️ 架构拆解:MVT 是个啥?)
- [🚀 核心组件速览](#🚀 核心组件速览)
-
- [1. 强大的 ORM](#1. 强大的 ORM)
- [2. 自动生成的 Admin 后台](#2. 自动生成的 Admin 后台)
- [3. 中间件 (Middleware) 与安全](#3. 中间件 (Middleware) 与安全)
- [⚡ Django 在 2026 年的进化:异步 (Async)](#⚡ Django 在 2026 年的进化:异步 (Async))
- [💣 常见误区与避坑指南](#💣 常见误区与避坑指南)
- [📝 总结与建议](#📝 总结与建议)
- [02:深挖 Django MVT 架构 ------ 告别"流水账"代码](#02:深挖 Django MVT 架构 —— 告别“流水账”代码)
-
- [🤔 灵魂拷问:MVT 和 MVC 到底啥关系?](#🤔 灵魂拷问:MVT 和 MVC 到底啥关系?)
- [🔍 深度拆解:三巨头各司其职](#🔍 深度拆解:三巨头各司其职)
-
- [1. Model (模型) ------ 仓库与账本](#1. Model (模型) —— 仓库与账本)
- [2. View (视图) ------ 大堂经理(核心指挥官)](#2. View (视图) —— 大堂经理(核心指挥官))
- [3. Template (模板) ------ 摆盘厨师](#3. Template (模板) —— 摆盘厨师)
- [🔄 数据流向图:一个请求的旅程](#🔄 数据流向图:一个请求的旅程)
- [⚠️ 常见架构陷阱(避坑指南)](#⚠️ 常见架构陷阱(避坑指南))
-
- [1. "胖视图" (Fat Views)](#1. “胖视图” (Fat Views))
- [2. "胖模板" (Fat Templates)](#2. “胖模板” (Fat Templates))
- [3. Model 依赖 Request](#3. Model 依赖 Request)
- [🛠️ 最佳实践:如何组织你的 MVT 代码?](#🛠️ 最佳实践:如何组织你的 MVT 代码?)
- [📝 总结](#📝 总结)
- [03:FBV vs CBV ------ 函数式与面向对象的终极对决](#03:FBV vs CBV —— 函数式与面向对象的终极对决)
-
- [⚔️ 第一回合:初识两位选手](#⚔️ 第一回合:初识两位选手)
-
- [1. FBV (Function-Based Views) ------ 简洁的实干家](#1. FBV (Function-Based Views) —— 简洁的实干家)
- [2. CBV (Class-Based Views) ------ 强大的架构师](#2. CBV (Class-Based Views) —— 强大的架构师)
- [🥊 第二回合:深度对比与核心差异](#🥊 第二回合:深度对比与核心差异)
- [💡 什么时候选谁?(决策指南)](#💡 什么时候选谁?(决策指南))
-
- [✅ 选择 FBV 的场景:](#✅ 选择 FBV 的场景:)
- [✅ 选择 CBV 的场景:](#✅ 选择 CBV 的场景:)
- [⚠️ 常见误区与避坑](#⚠️ 常见误区与避坑)
-
- [1. "CBV 一定比 FBV 高级"](#1. “CBV 一定比 FBV 高级”)
- [2. 忘记 `.as_view()`](#2. 忘记
.as_view()) - [3. 在 CBV 中滥用 `init`](#3. 在 CBV 中滥用
__init__) - [4. 难以调试的继承链](#4. 难以调试的继承链)
- [🛠️ 最佳实践:混合使用的艺术](#🛠️ 最佳实践:混合使用的艺术)
- [📝 总结](#📝 总结)
- [📝 总结](#📝 总结)
01:Django ------ 那个"自带电池"的 Web 巨无霸
写在前面 :
今天是 2026 年 3 月 15 日。如果你刚入坑 Python Web,或者从 Flask/FastAPI 转过来想看看"老牌劲旅"长啥样,这篇笔记就是为你准备的。咱们不堆砌文档,只聊核心逻辑、避坑指南和它在 2026 年依然能打的原因。
🧐 为什么是 Django?
在 Python Web 的江湖里,如果说 FastAPI 是那个身手敏捷、专攻异步高并发的"忍者",Flask 是灵活多变、需要什么装什么的"瑞士军刀",那 Django 绝对就是那个全副武装、自带后勤部的"重装坦克"。
官方那句口号 "The web framework for perfectionists with deadlines"(为有最后期限的完美主义者打造的 Web 框架),直到 2026 年依然是真理。
核心哲学:Batteries Included(自带电池)
你不需要像在其他微框架里那样,为了一个用户认证去满世界找库,为了一个后台管理界面去写三天前端。Django 告诉你:
- 需要 ORM?内置了,而且很强。
- 需要 Admin 后台?内置了,一行代码生成。
- 需要表单验证、国际化、缓存、安全防御(CSRF, SQL Injection)?全都有。
适用场景:
- 快速构建内容管理系统(CMS)、电商网站、SaaS 平台。
- 团队开发,需要统一的代码规范和结构。
- 项目需求复杂,涉及用户系统、权限管理、复杂的数据库关系。
🏗️ 架构拆解:MVT 是个啥?
很多教程说 Django 是 MVC(Model-View-Controller),其实不准确。Django 采用的是 MVT (Model-View-Template) 模式。
咱们用开餐厅来类比一下:
-
Model (模型) = 仓库管理员
- 负责跟数据库打交道。你定义好数据结构(比如
User,Article),它帮你生成 SQL,帮你存数据、取数据。你不用写一句SELECT * FROM ...。 - 2026 视角:现在的 Django ORM 对异步支持已经非常成熟,甚至支持更复杂的向量数据库查询(配合 AI 应用)。
- 负责跟数据库打交道。你定义好数据结构(比如
-
View (视图) = 大堂经理
- 这是核心逻辑层。它接收用户的请求(比如"我要看最新文章"),指挥 Model 去拿数据,然后决定把数据交给哪个 Template 去渲染,或者直接返回 JSON。
- 注意:Django 的 View 对应的是 MVC 里的 Controller 角色。
-
Template (模板) = 摆盘厨师
- 负责把数据"摆盘"成 HTML。Django 的模板语言(DTL)很简单,禁止在模板里写复杂逻辑,强制你把业务逻辑留在 View 里。
- 现状:虽然现在前后端分离很流行(Django 只当 API 后端),但在做 SEO 友好的页面或内部管理系统时,DTL 依然香得不得了。
🚀 核心组件速览
1. 强大的 ORM
Django 的 ORM 可能是 Python 界最易用的之一。
python
# 定义模型
class Article(models.Model):
title = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
is_published = models.BooleanField(default=False)
# 查询数据(链式调用,可读性极强)
recent_articles = Article.objects.filter(is_published=True).order_by('-pub_date')[:5]
最佳实践 :警惕 N+1 查询问题 。在循环中访问外键字段是新手最容易踩的坑。记得用 select_related (一对一/多对一) 和 prefetch_related (多对多) 来优化。
2. 自动生成的 Admin 后台
这是 Django 的杀手锏。在 admin.py 里注册模型:
python
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
list_display = ('title', 'pub_date', 'is_published')
search_fields = ['title']
刷新页面,一个功能完备、支持搜索、过滤、增删改查的后台就诞生了。对于内部运营工具,这能节省 80% 的开发时间。
3. 中间件 (Middleware) 与安全
Django 的请求处理像一个洋葱,中间件层层包裹。
- 安全:默认开启 CSRF 保护、SQL 注入防护、XSS 过滤。
- 会话:自带的 Session 框架非常稳健。
- 自定义:你可以轻松写一个中间件来处理全局日志、黑白名单或者特殊的 Header 注入。
⚡ Django 在 2026 年的进化:异步 (Async)
如果在 2023 年之前,大家还会纠结 "Django 不支持异步",那现在这个顾虑可以打消了。
从 Django 3.0 开始引入 ASGI 支持,到 4.x 和 5.x 版本的完善,Django 现在已经是一个原生支持异步的框架。
async def view(request):完全可用。- ORM 的大部分操作也支持了
await(例如await Article.objects.aget(id=1))。 - 这意味着你可以用 Django 编写高性能的 WebSocket 应用(配合 Django Channels)或者高并发 API,而不再必须依赖 FastAPI。
误区提醒 :虽然支持异步,但不要为了异步而异步。如果你的业务逻辑主要是 CPU 密集型或者简单的数据库 CRUD,同步代码依然稳定且易于调试。只有在涉及大量 IO 等待(如调用外部慢速 API、长轮询)时,异步的优势才明显。
💣 常见误区与避坑指南
-
"上帝对象" God Object
- 现象 :把所有逻辑都塞进
views.py或者models.py的一个大方法里。 - 对策 :学习使用 Services 层 或 Forms/Serializers 来拆分业务逻辑。保持 View 轻薄,只负责流程控制。
- 现象 :把所有逻辑都塞进
-
过度依赖 Admin
- 现象:直接把 Admin 暴露给最终用户使用,或者在 Admin 里写复杂业务逻辑。
- 对策:Admin 是给管理员用的。面向用户的功能请老老实实写 View 和 API。
-
忽视迁移文件 (Migrations) 冲突
- 现象:多人协作时,合并代码后迁移文件冲突,导致数据库结构错乱。
- 对策 :提交代码前务必在本地 rebase 并重新生成迁移文件。CI/CD 流程中必须包含
makemigrations --check。
-
前后端分离时的 CORS 问题
- 现象:Vue/React 前端调不通 Django 接口。
- 对策 :安装
django-cors-headers,并在settings.py中正确配置CORS_ALLOWED_ORIGINS。别在生产环境设置CORS_ALLOW_ALL_ORIGINS = True!
📝 总结与建议
Django 就像一个成熟的中年工程师,稳重、全能、经验丰富。它可能没有 FastAPI 那么"极客"和"轻快",但在构建大型、复杂、需要长期维护的企业级应用时,它的生态完整性和稳定性是无与伦比的。
我的建议:
- 如果你是初学者,Django 是理解 Web 全貌(从 DB 到 HTML)的最佳起点。
- 如果你在做快速原型(MVP)或内部工具,Django + DRF (Django Rest Framework) 依然是首选。
- 如果你追求极致的微服务性能或纯实时通信,可以考虑 FastAPI,但别忘了 Django 也能做。
02:深挖 Django MVT 架构 ------ 告别"流水账"代码
前情提要 :
在上一篇里我们提到了 Django 的 MVT 模式。很多初学者(包括当年的我)第一次听到 MVT 时,脑子里全是问号:"MVC 我懂,这 MVT 又是哪路神仙?是不是 Django 发明家为了显得与众不同硬造的词?"
今天这篇笔记,咱们就拿着手术刀,把 MVT (Model-View-Template) 彻底解剖开。搞懂了它,你写的 Django 代码才不会变成"一锅粥",而是井井有条的模块化工程。
🤔 灵魂拷问:MVT 和 MVC 到底啥关系?
首先得澄清一个巨大的误区:Django 的 MVT 本质上就是 MVC 的变种。
-
MVC (Model-View-Controller):
- Model: 数据层。
- View: 展示层(用户看到的界面)。
- Controller: 逻辑层(接收请求,调用 Model,选择 View)。
-
Django 的 MVT (Model-View-Template):
- Model: 数据层(同 MVC)。
- View : 逻辑层(注意!这里的 View 对应 MVC 的 Controller)。
- Template: 展示层(对应 MVC 的 View)。
为什么 Django 要这么叫?
因为 Django 的哲学是:"视图(View)应该只负责业务逻辑,而不应该包含 HTML 展示代码" 。
在传统的 MVC 框架(如早期的 Java Struts 或 PHP Laravel)中,"View" 往往指的是那个包含 HTML 的文件。但在 Django 看来,那个 HTML 文件应该叫 Template ,而负责决定"用哪个模板、传什么数据"的那个函数/类,才是 View。
💡 一句话总结 :
Django 的 View = MVC 的 Controller
Django 的 Template = MVC 的 View
| 经典 MVC (Model-View-Controller) | Django MTV (Model-Template-View) | 通俗理解 |
|---|---|---|
| Model (模型) | Model (模型) | 没变。都负责数据存取、数据库交互。 |
| View (视图) | Template (模板) | 名字互换了。MVC 的 View 负责"怎么展示页面",MTV 里这叫 Template。 |
| Controller (控制器) | View (视图) | 名字互换了。MVC 的 Controller 负责"业务逻辑",MTV 里这叫 View。 |
🔍 深度拆解:三巨头各司其职
咱们还是用 "餐厅点餐" 的例子,这次把流程细化到每一个动作。
1. Model (模型) ------ 仓库与账本
职责:数据的唯一真理来源。它定义数据结构,负责与数据库交互(CRUD),并包含数据验证逻辑。
-
它在做什么:
- 定义字段(
CharField,IntegerField等)。 - 管理数据库表结构(通过 Migration)。
- 提供 ORM 接口(
.objects.all(),.filter())。 - 关键点:Model 不应该知道 HTTP 请求是什么,也不应该知道 HTML 长啥样。它只关心数据。
- 定义字段(
-
代码一瞥 (
models.py):pythonclass Order(models.Model): customer = models.ForeignKey('Customer', on_delete=models.CASCADE) total_amount = models.DecimalField(max_digits=10, decimal_places=2) status = models.CharField(choices=[('P', 'Pending'), ('D', 'Delivered')], max_length=1) def is_expensive(self): # 业务逻辑可以放在 Model 里 return self.total_amount > 1000
2. View (视图) ------ 大堂经理(核心指挥官)
职责:接收 HTTP 请求,处理业务逻辑,获取数据,决定返回什么响应。
-
它在做什么:
- 解析
request对象(GET/POST 参数,User 信息)。 - 调用 Model 获取或保存数据。
- 进行复杂的业务判断(比如:用户是否登录?库存够不够?)。
- 选择 Template 并填入数据(Context),或者直接返回 JSON/XML。
- 关键点 :View 应该是"薄"的。如果逻辑太复杂,应该抽取到
services.py或forms.py中,不要把所有代码都堆在 View 函数里。
- 解析
-
代码一瞥 (
views.py):pythonfrom django.shortcuts import render, get_object_or_404 from .models import Order def order_detail(request, order_id): # 1. 接收请求 & 权限检查 if not request.user.is_authenticated: return redirect('login') # 2. 调用 Model 获取数据 order = get_object_or_404(Order, pk=order_id, customer=request.user) # 3. 准备上下文数据 (Context) context = { 'order': order, 'is_vip': order.customer.is_vip, 'message': '感谢您的订单!' if order.status == 'D' else '正在配送中' } # 4. 选择 Template 并渲染 return render(request, 'orders/detail.html', context)
3. Template (模板) ------ 摆盘厨师
职责:负责数据的最终展示(HTML 生成)。它只负责"怎么好看",不负责"数据从哪来"或"逻辑怎么算"。
-
它在做什么:
- 使用 Django Template Language (DTL) 展示变量
{``{ variable }}。 - 使用简单的控制流
{% if %},{% for %}。 - 继承布局 (
{% extends "base.html" %}) 以保持站点风格统一。 - 关键点 :严禁在模板里写复杂逻辑 (比如数据库查询、数学运算、API 调用)。如果模板里出现了
{% call_api %}这种操作,那就是架构坏了。
- 使用 Django Template Language (DTL) 展示变量
-
代码一瞥 (
templates/orders/detail.html):html{% extends "base.html" %} {% block content %} <h1>订单 #{{ order.id }}</h1> <p>客户:{{ order.customer.name }}</p> <p>总金额:${{ order.total_amount }}</p> {% if is_vip %} <span class="badge gold">VIP 客户专享</span> {% endif %} <div class="status-box"> {{ message }} </div> <!-- 循环展示订单项 --> <ul> {% for item in order.items.all %} <li>{{ item.name }} - ${{ item.price }}</li> {% endfor %} </ul> {% endblock %}
🔄 数据流向图:一个请求的旅程
当一个用户访问 http://mysite.com/orders/123/ 时,MVT 是这样协作的:
- URL Dispatcher (门卫): 解析 URL,发现匹配
path('orders/<int:id>/', views.order_detail),于是把请求交给order_detailView。 - View (经理):
- 接到请求,发现 ID 是 123。
- 喊 Model: "嘿,把 ID 为 123 的订单给我查出来,顺便看看是不是当前用户的。"
- Model 去数据库查好数据,返回给 View。
- View 整理好数据字典(Context)。
- View 对 Template 说:"这是
detail.html,把这些数据填进去,渲染成 HTML 给我。"
- Template (厨师): 拿到数据和 HTML 骨架,把
{``{ order.total_amount }}替换成真实数字,生成最终的 HTML 字符串。 - View (经理): 把生成的 HTML 包装成
HttpResponse,返回给用户浏览器。
⚠️ 常见架构陷阱(避坑指南)
在实际开发中,MVT 很容易走样,以下是几个典型的"反模式":
1. "胖视图" (Fat Views)
- 症状 :
views.py文件几千行,里面充斥着 SQL 拼接(甚至不用 ORM)、复杂的数学计算、第三方 API 调用。 - 后果:难以测试,难以复用,一个人不敢改,改了全崩。
- 解法 :
- 把数据库复杂查询逻辑移到 Model Manager 或 QuerySet 自定义方法中。
- 把业务逻辑(如计算价格、发送通知)抽取到独立的 Service Layer (
services.py)。 - 把表单处理逻辑交给 Forms 或 Serializers。
- 目标:View 只保留流程控制(if/else, try/catch, return)。
2. "胖模板" (Fat Templates)
- 症状 :模板里充满了
{% if %}{% elif %}{% else %}的嵌套地狱,甚至尝试在模板里做列表排序、字符串切割。 - 后果:前端逻辑和后端逻辑耦合,设计师改个页面容易把逻辑搞挂。
- 解法 :
- 使用 Custom Template Tags 或 Filters 来处理简单的格式化逻辑。
- 复杂的展示逻辑(比如"哪些菜单项该高亮")应该在 View 里算好,作为布尔值传给模板。
3. Model 依赖 Request
- 症状 :在
models.py里直接引用request.user或者request.session。 - 后果:Model 变得无法脱离 Web 环境运行(比如写脚本跑定时任务时会报错)。
- 解法 :Model 应该是纯粹的 Python 类。如果需要当前用户信息,应该通过 View 传递给 Model 的方法参数,而不是让 Model 自己去全局找
request。
🛠️ 最佳实践:如何组织你的 MVT 代码?
一个健康的 Django App 结构应该是这样的:
text
myapp/
├── models.py # 纯数据定义,包含简单的业务方法 (__str__, is_valid)
├── queries.py # (可选) 复杂的 QuerySet 自定义管理器
├── forms.py # 表单验证逻辑 (HTML 表单或 API 输入验证)
├── services.py # (强烈推荐) 核心业务逻辑,如支付处理、邮件发送
├── views.py # 薄薄的控制器:接收请求 -> 调 service/form -> 选 template
├── urls.py # URL 路由映射
├── templates/ # 纯 HTML + DTL 标签,无复杂逻辑
└── tests.py # 针对上述所有层的单元测试
黄金法则:
- Model 知道"数据是什么"。
- View 知道"发生了什么"。
- Template 知道"看起来怎么样"。
- Service (额外层) 知道"怎么做"。
📝 总结
MVT 不仅仅是一个名词,它是 Django 强制你进行关注点分离 (Separation of Concerns) 的手段。
- 当你想改数据库结构时,只动 Model。
- 当你想改业务流程时,只动 View (或 Service)。
- 当你想改页面样式时,只动 Template。
只要守住这条界线,你的 Django 项目就能在几年后依然易于维护。反之,如果界线模糊,项目很快就会变成难以收拾的"大泥球"。
03:FBV vs CBV ------ 函数式与面向对象的终极对决
前情提要 :
在上一篇里,我们搞懂了 MVT 架构中 View 的核心地位。但当你打开 Django 的源码或者查看别人的项目时,你会发现 View 有两种截然不同的写法:
- FBV (Function-Based Views):普通的 Python 函数,简单直接。
- CBV (Class-Based Views):基于类的视图,结构复杂但功能强大。
新手往往在这里迷失:"我到底该用哪个?是不是 CBV 更高级所以必须用它?"
今天这篇笔记,咱们就来一场 FBV vs CBV 的深度剖析,帮你找到最适合当前场景的武器。
⚔️ 第一回合:初识两位选手
1. FBV (Function-Based Views) ------ 简洁的实干家
FBV 就是最纯粹的 Python 函数。它接收一个 request 对象,返回一个 response 对象。
代码示例 (views.py):
python
from django.shortcuts import render, get_object_or_404
from .models import Article
def article_detail(request, pk):
"""
显示文章详情页
"""
# 1. 获取数据
article = get_object_or_404(Article, pk=pk)
# 2. 处理逻辑 (比如增加阅读量)
if request.method == 'GET':
article.views_count += 1
article.save()
# 3. 渲染返回
return render(request, 'articles/detail.html', {'article': article})
特点:
- 直观:代码从上到下执行,逻辑流清晰,像读故事一样。
- 灵活:你想怎么写就怎么写,没有任何框架强加的类继承结构。
- 装饰器友好 :想加权限验证?直接
@login_required包在外面,简单粗暴。
2. CBV (Class-Based Views) ------ 强大的架构师
CBV 将视图逻辑封装在类中。Django 提供了一系列内置的通用视图(Generic Views),如 ListView, DetailView, CreateView 等,能极大减少重复代码。
代码示例 (views.py):
python
from django.views.generic import DetailView
from .models import Article
class ArticleDetailView(DetailView):
model = Article
template_name = 'articles/detail.html'
context_object_name = 'article'
def get(self, request, *args, **kwargs):
# 重写 get 方法处理额外逻辑
response = super().get(request, *args, **kwargs)
# 增加阅读量 (注意:通常应该在 form 提交或专门接口做,这里仅演示)
self.object.views_count += 1
self.object.save()
return response
URL 配置 (urls.py):
python
# 注意这里要调用 .as_view() 方法
path('article/<int:pk>/', ArticleDetailView.as_view(), name='article_detail')
特点:
- 复用性高:通过继承,轻松复用 Django 内置的 CRUD 逻辑。
- 结构化 :强制将 HTTP 方法分发到
get(),post(),put()等不同方法中,符合 RESTful 风格。 - ** mixins 机制**:可以通过多重继承轻松组合功能(如
LoginRequiredMixin+DetailView)。
🥊 第二回合:深度对比与核心差异
| 特性 | FBV (函数视图) | CBV (类视图) |
|---|---|---|
| 代码量 | 对于简单逻辑,代码较少;复杂 CRUD 需手写大量重复代码。 | 对于标准 CRUD,代码极少;自定义复杂逻辑时代码可能分散。 |
| 可读性 | 高。线性执行,容易追踪调试。 | 中/低。逻辑分散在不同方法中,需要理解继承链和 Mixin。 |
| HTTP 方法处理 | 需要在函数内手动 if request.method == 'POST': ... |
自动分发。定义 def post(self): 即可处理 POST 请求。 |
| 装饰器使用 | 直接使用 @decorator。 |
需要使用 method_decorator 或在 dispatch 方法中处理,稍显繁琐。 |
| 状态保持 | 无状态,每次调用都是新的函数栈。 | 类实例化后,可以在 self 上保存状态(虽然 Django 推荐无状态,但技术上可行)。 |
| 适用场景 | 简单页面、非标准逻辑、API 端点(配合 DRF 前)、脚本式任务。 | 标准的增删改查 (CRUD)、列表页、详情页、需要高度复用的组件。 |
💡 什么时候选谁?(决策指南)
这是大家最关心的问题。我的建议是:不要教条主义,按需选择。
✅ 选择 FBV 的场景:
- 逻辑非常简单:只是渲染一个静态页面,或者做一个简单的重定向。
- 非标准流程:业务逻辑非常独特,无法套用 Django 的通用视图模式(比如一个复杂的向导式表单,每一步都依赖上一步的状态)。
- 调试优先 :你需要极其清晰地看到每一行代码的执行顺序,不想去猜 Django 内部是怎么调用
dispatch的。 - API 开发 (DRF):虽然现在 DRF 也推崇 CBV (ViewSets),但在写一些特殊的 Function-based API View 时,FBV 依然很清爽。
✅ 选择 CBV 的场景:
-
标准 CRUD :你需要快速实现一个文章的列表、详情、创建、更新、删除。用
ListView,DetailView,CreateView等,几行代码搞定,还能自动处理分页、表单验证。 -
需要 Mixins :比如"只有登录用户才能访问"、"只有管理员才能编辑"。你可以这样写:
pythonclass ArticleUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView): model = Article fields = ['title', 'content'] def test_func(self): obj = self.get_object() return self.request.user == obj.author这种组合式继承在 FBV 里需要嵌套多个装饰器, readability 会下降。
-
大型项目规范:团队希望统一视图结构,强制区分 GET/POST 处理逻辑。
⚠️ 常见误区与避坑
1. "CBV 一定比 FBV 高级"
错! 很多新手为了显得"专业",强行把简单的函数改成类,结果引入了不必要的复杂性。
原则:如果 FBV 能清晰解决问题,就别用 CBV。简单即是美。
2. 忘记 .as_view()
在 urls.py 中注册 CBV 时,必须 调用 .as_view() 方法。
- ❌ 错误:
path('...', MyView, ...) - ✅ 正确:
path('...', MyView.as_view(), ...)
原因 :URL 解析器期望一个可调用对象(函数),而类本身不可调用(除非实现了__call__,但 Django 的设计是通过as_view()返回一个闭包函数来实例化类并处理请求)。
3. 在 CBV 中滥用 __init__
大坑! 不要在 CBV 的 __init__ 方法里访问 request 或 kwargs。
原因 :类在模块加载时只初始化一次,而 request 是每次请求不同的。
正确做法 :在 dispatch, get, post 等方法中访问 self.request。
4. 难以调试的继承链
当你继承了一个 Generic View,又混入了三个 Mixin,出错了却不知道是哪个环节的问题。
对策 :善用 print(self.__class__.__mro__) 查看方法解析顺序 (MRO)。尽量保持继承层级扁平,不要过度设计。
🛠️ 最佳实践:混合使用的艺术
在实际的 2026 年 Django 项目中,我们通常是混合使用的:
-
基础 CRUD 用 CBV :
利用
ListView,DetailView,CreateView,UpdateView,DeleteView快速搭建骨架。这能节省 70% 的样板代码。 -
特殊逻辑用 FBV 或 重写 CBV 方法:
- 如果某个页面的逻辑稍微特殊一点,优先尝试重写 CBV 的方法 (如
get_context_data,form_valid)。 - 如果逻辑实在太复杂,或者完全不符合通用视图的流程,果断换回 FBV,或者在该 URL 下单独写一个 FBV。
- 如果某个页面的逻辑稍微特殊一点,优先尝试重写 CBV 的方法 (如
-
DRF 中的 ViewSets :
如果你在使用 Django Rest Framework,那么
ModelViewSet是 CBV 的极致体现。它将整套 CRUD 逻辑封装得连 URL 配置都简化了(Router)。在这种情况下,坚持使用 CBV 风格收益最大。
重构技巧 :
很多时候,我们会先用 FBV 快速实现一个功能(MVP 阶段),当发现代码中有大量重复逻辑(比如多个视图都在做类似的权限检查和数据预处理)时,再将其重构为 CBV + Mixin。
📝 总结
- FBV 是显式 的,适合特异化 场景,胜在透明。
- CBV 是隐式 的,适合标准化 场景,胜在复用。
,优先尝试重写 CBV 的方法 (如 get_context_data, form_valid)。
- 如果逻辑实在太复杂,或者完全不符合通用视图的流程,果断换回 FBV,或者在该 URL 下单独写一个 FBV。
- DRF 中的 ViewSets :
如果你在使用 Django Rest Framework,那么ModelViewSet是 CBV 的极致体现。它将整套 CRUD 逻辑封装得连 URL 配置都简化了(Router)。在这种情况下,坚持使用 CBV 风格收益最大。
重构技巧 :
很多时候,我们会先用 FBV 快速实现一个功能(MVP 阶段),当发现代码中有大量重复逻辑(比如多个视图都在做类似的权限检查和数据预处理)时,再将其重构为 CBV + Mixin。
📝 总结
- FBV 是显式 的,适合特异化 场景,胜在透明。
- CBV 是隐式 的,适合标准化 场景,胜在复用。