Django的MTV架构

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) 模式。

咱们用开餐厅来类比一下:

  1. Model (模型) = 仓库管理员

    • 负责跟数据库打交道。你定义好数据结构(比如 User, Article),它帮你生成 SQL,帮你存数据、取数据。你不用写一句 SELECT * FROM ...
    • 2026 视角:现在的 Django ORM 对异步支持已经非常成熟,甚至支持更复杂的向量数据库查询(配合 AI 应用)。
  2. View (视图) = 大堂经理

    • 这是核心逻辑层。它接收用户的请求(比如"我要看最新文章"),指挥 Model 去拿数据,然后决定把数据交给哪个 Template 去渲染,或者直接返回 JSON。
    • 注意:Django 的 View 对应的是 MVC 里的 Controller 角色。
  3. 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、长轮询)时,异步的优势才明显。

💣 常见误区与避坑指南

  1. "上帝对象" God Object

    • 现象 :把所有逻辑都塞进 views.py 或者 models.py 的一个大方法里。
    • 对策 :学习使用 Services 层Forms/Serializers 来拆分业务逻辑。保持 View 轻薄,只负责流程控制。
  2. 过度依赖 Admin

    • 现象:直接把 Admin 暴露给最终用户使用,或者在 Admin 里写复杂业务逻辑。
    • 对策:Admin 是给管理员用的。面向用户的功能请老老实实写 View 和 API。
  3. 忽视迁移文件 (Migrations) 冲突

    • 现象:多人协作时,合并代码后迁移文件冲突,导致数据库结构错乱。
    • 对策 :提交代码前务必在本地 rebase 并重新生成迁移文件。CI/CD 流程中必须包含 makemigrations --check
  4. 前后端分离时的 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):

    python 复制代码
    class 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.pyforms.py 中,不要把所有代码都堆在 View 函数里。
  • 代码一瞥 (views.py):

    python 复制代码
    from 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 %} 这种操作,那就是架构坏了。
  • 代码一瞥 (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 是这样协作的:

  1. URL Dispatcher (门卫): 解析 URL,发现匹配 path('orders/<int:id>/', views.order_detail),于是把请求交给 order_detail View
  2. View (经理):
    • 接到请求,发现 ID 是 123。
    • Model: "嘿,把 ID 为 123 的订单给我查出来,顺便看看是不是当前用户的。"
    • Model 去数据库查好数据,返回给 View。
    • View 整理好数据字典(Context)。
    • View 对 Template 说:"这是 detail.html,把这些数据填进去,渲染成 HTML 给我。"
  3. Template (厨师): 拿到数据和 HTML 骨架,把 {``{ order.total_amount }} 替换成真实数字,生成最终的 HTML 字符串。
  4. View (经理): 把生成的 HTML 包装成 HttpResponse,返回给用户浏览器。

⚠️ 常见架构陷阱(避坑指南)

在实际开发中,MVT 很容易走样,以下是几个典型的"反模式":

1. "胖视图" (Fat Views)

  • 症状views.py 文件几千行,里面充斥着 SQL 拼接(甚至不用 ORM)、复杂的数学计算、第三方 API 调用。
  • 后果:难以测试,难以复用,一个人不敢改,改了全崩。
  • 解法
    • 把数据库复杂查询逻辑移到 Model ManagerQuerySet 自定义方法中。
    • 把业务逻辑(如计算价格、发送通知)抽取到独立的 Service Layer (services.py)。
    • 把表单处理逻辑交给 FormsSerializers
    • 目标:View 只保留流程控制(if/else, try/catch, return)。

2. "胖模板" (Fat Templates)

  • 症状 :模板里充满了 {% if %}{% elif %}{% else %} 的嵌套地狱,甚至尝试在模板里做列表排序、字符串切割。
  • 后果:前端逻辑和后端逻辑耦合,设计师改个页面容易把逻辑搞挂。
  • 解法
    • 使用 Custom Template TagsFilters 来处理简单的格式化逻辑。
    • 复杂的展示逻辑(比如"哪些菜单项该高亮")应该在 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 有两种截然不同的写法:

  1. FBV (Function-Based Views):普通的 Python 函数,简单直接。
  2. 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 的场景:

  1. 逻辑非常简单:只是渲染一个静态页面,或者做一个简单的重定向。
  2. 非标准流程:业务逻辑非常独特,无法套用 Django 的通用视图模式(比如一个复杂的向导式表单,每一步都依赖上一步的状态)。
  3. 调试优先 :你需要极其清晰地看到每一行代码的执行顺序,不想去猜 Django 内部是怎么调用 dispatch 的。
  4. API 开发 (DRF):虽然现在 DRF 也推崇 CBV (ViewSets),但在写一些特殊的 Function-based API View 时,FBV 依然很清爽。

✅ 选择 CBV 的场景:

  1. 标准 CRUD :你需要快速实现一个文章的列表、详情、创建、更新、删除。用 ListView, DetailView, CreateView 等,几行代码搞定,还能自动处理分页、表单验证。

  2. 需要 Mixins :比如"只有登录用户才能访问"、"只有管理员才能编辑"。你可以这样写:

    python 复制代码
    class 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 会下降。

  3. 大型项目规范:团队希望统一视图结构,强制区分 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__ 方法里访问 requestkwargs
原因 :类在模块加载时只初始化一次,而 request 是每次请求不同的。
正确做法 :在 dispatch, get, post 等方法中访问 self.request

4. 难以调试的继承链

当你继承了一个 Generic View,又混入了三个 Mixin,出错了却不知道是哪个环节的问题。
对策 :善用 print(self.__class__.__mro__) 查看方法解析顺序 (MRO)。尽量保持继承层级扁平,不要过度设计。

🛠️ 最佳实践:混合使用的艺术

在实际的 2026 年 Django 项目中,我们通常是混合使用的:

  1. 基础 CRUD 用 CBV

    利用 ListView, DetailView, CreateView, UpdateView, DeleteView 快速搭建骨架。这能节省 70% 的样板代码。

  2. 特殊逻辑用 FBV 或 重写 CBV 方法

    • 如果某个页面的逻辑稍微特殊一点,优先尝试重写 CBV 的方法 (如 get_context_data, form_valid)。
    • 如果逻辑实在太复杂,或者完全不符合通用视图的流程,果断换回 FBV,或者在该 URL 下单独写一个 FBV。
  3. 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。
  1. DRF 中的 ViewSets
    如果你在使用 Django Rest Framework,那么 ModelViewSet 是 CBV 的极致体现。它将整套 CRUD 逻辑封装得连 URL 配置都简化了(Router)。在这种情况下,坚持使用 CBV 风格收益最大。

重构技巧

很多时候,我们会先用 FBV 快速实现一个功能(MVP 阶段),当发现代码中有大量重复逻辑(比如多个视图都在做类似的权限检查和数据预处理)时,再将其重构为 CBV + Mixin。

📝 总结

  • FBV显式 的,适合特异化 场景,胜在透明
  • CBV隐式 的,适合标准化 场景,胜在复用
相关推荐
小江的记录本2 小时前
【AOP】AOP-面向切面编程 (系统性知识体系全解)
java·前端·后端·python·网络协议·青少年编程·代理模式
小鸡吃米…2 小时前
调试线程应用程序
开发语言·python
MasonYyp2 小时前
简单使用代码沙箱技术
python
梨落秋霜2 小时前
Python入门篇【连接数据库】
数据库·python·oracle
weixin_440401692 小时前
网络机器人(爬虫)+Xpath+网页F12+爬取电影Top100
爬虫·python·机器人
伊甸32 小时前
Python numpy笔记01
笔记·python·数据分析
啊哈哈哈哈哈啊哈哈2 小时前
cv实践——银行卡数字识别
python·opencv·计算机视觉
2401_889884662 小时前
深入理解Python的if __name__ == ‘__main__‘
jvm·数据库·python
TaiKuLaHa2 小时前
数据仓库处理架构: lambda架构、kappa架构
数据仓库·架构