欢迎光临
我们一直在努力

【Web应用开发笔记】Django笔记7-2:从零开始理解 Django

前言

说实话,对于没有基础的人来说,Django 是一个用起来简单的工具。
但正如程序员们熟知的那条规律一样:

  • 高度封装意味着简单但灵活度有限
  • 高度灵活意味着复杂难上手

所以,在开始我的第一个练手项目“博客网站”之后,如此简洁的代码让我彻底懵逼。
毕竟我写了多年 C++,工作中面临大量的逻辑实现,现在突然遇到一个把大量逻辑封装到内部的工具,我很难不好奇里面究竟发生了什么。

因此,本文将从最基础的网页访问出发,梳理 Django 干了哪些事。
让我们从最简单的一个场景开始:

  • 用户在浏览器输入网址,然后打开了一个网页,看到了网页的内容。

1. 数据在计算机网络中的传输过程

1.1 类比

网络世界对应角色类比
浏览器构造 HTTP 请求 客户端 (Client) 为了向朋友要相册,你写信,投入邮筒
互联网路由转发(TCP/IP) 基础设施 邮局分拣、运输
服务器接收并解析请求 服务器 (Server) 朋友收到信,查看内容
Django 处理逻辑,生成响应 后端应用 朋友准备相册,写回信
响应数据沿原路返回 网络传输 回信寄回北京
浏览器渲染 HTML/CSS/JS 客户端展示 你收到信,打开相册

1.2 步骤拆解

网络世界对应包含的微观步骤网络分层角色
浏览器构造 HTTP 请求 1. 浏览器解析 URL2. 组装 HTTP 请求报文(方法、路径、Headers、Body)3. 查询本地缓存 应用层 客户端 (Client)
互联网路由转发(TCP/IP) 1. DNS 查询(域名 → IP 地址)2. TCP 三次握手(建立可靠连接)3. TLS/SSL 握手(HTTPS 加密,可选)4. IP 路由转发(数据包跨网络跳转传输)5. 物理层光电信号传输 应用层 (DNS)传输层 (TCP)网络层 (IP)数据链路层/物理层 基础设施
服务器接收并解析请求 1. WSGI 服务器接收 TCP 连接2. 解析 HTTP 请求报文 → 封装为 HttpRequest 对象3. 传递给 Django 框架 应用层 服务器 (Server)
Django 处理逻辑,生成响应 1. URL 路由(匹配 urls.py)2. 视图处理(views.py 执行业务逻辑)3. 模型交互(models.py ORM 查询数据库)4. 模板渲染(templates 生成 HTML)5. 构造 HttpResponse 对象 应用层 后端应用
响应数据沿原路返回 1. WSGI 服务器发送 HTTP 响应报文2. TCP 可靠传输(确认机制、重传机制)3. IP 路由反向跳转4. 浏览器接收响应 应用层 (HTTP)传输层 (TCP)网络层 (IP)数据链路层/物理层 网络传输
浏览器渲染 HTML/CSS/JS 1. 解析 HTML → 构建 DOM 树2. 解析 CSS → 构建 CSSOM 树3. 执行 JavaScript(可能修改 DOM)4. 合并渲染树 → 计算布局5. 绘制页面(Paint)6. 加载额外资源(图片、字体等,可能再次发起 HTTP 请求) 应用层(浏览器引擎) 客户端展示

1.3 关于网络分层

  • 模型 A:OSI 七层模型(理论完整版)
层级名称核心功能典型协议/设备
7 应用层 为用户程序提供网络服务 HTTP、FTP、SMTP、DNS
6 表示层 数据格式转换、加密解密 TLS/SSL、JPEG、ASCII
5 会话层 建立、管理、终止会话 NetBIOS、RPC
4 传输层 端到端可靠传输 TCP、UDP
3 网络层 逻辑寻址、路由选择 IP、ICMP、路由器
2 数据链路层 物理寻址、帧封装、差错检测 Ethernet、MAC、交换机
1 物理层 比特流传输、电气特性 光纤、电缆、集线器、网卡
  • 模型 B:TCP/IP 四层模型(实际应用版)
层级对应 OSI 层级核心功能我们的场景对应
应用层 5+6+7 层合并 应用协议 + 表示 + 会话 HTTP、DNS、TLS、Django
传输层 4 层 端到端连接 TCP 三次握手、可靠传输
网络层 3 层 逻辑寻址与路由 IP 地址、路由跳转
网络接口层 1+2 层合并 物理传输 + 数据链路 网卡、MAC、光纤、Wi-Fi
  • 每层 封装/解封装

【HTTP 报文】← 应用层:GET /blog/ HTTP/1.1

【TCP 段】 ← 传输层:加上源端口、目的端口、序列号

【IP 包】 ← 网络层:加上源 IP、目的 IP

【以太网帧】← 数据链路层:加上源 MAC、目的 MAC

【比特流】 ← 物理层:01010101… 电信号/光信号

发送时:从上到下,层层封装(加头部)
接收时:从下到上,层层解封装(拆头部)

  • 辨析

    • 上面的表里写:传输层 | TCP 三次握手、可靠传输
    • 实际上:三次握手的整体流程是多层配合完成的
    • 传输层:
      • 它知道 “如何保障数据传输稳定” 的规则
      • 并按照这套规则来发送相应的“信号”
      • 而这套规则俗称“三次握手”
    • 而其它层:不关心用什么手段来保障
      • 应用层:告诉传输层,我有一个“请求”
      • 传输层:我有一套保障数据可靠传输的流程,现在我先发个“询问信号”探探路
      • 网络层:现在有一个包需要我发到某个 IP,我来找路由
    • 所以,传输层 确实是 三次握手 这个过程的 leader
  • 整个过程可以称之为“栈式封装”

    • 每一层接收到上一层的“需求”
    • 需要完成本层的“工作”,这需要用到下一层
    • 给下一层发送“需求”,下一层返回“结果”
    • 根据下一层返回的“结果”,封装本层的工作“结果”
    • 本层的“结果”反馈给上一层
    • 整个过程就如同入栈、出栈一样

┌─────────────────────────────────────────┐
│ 应用层 (HTTP): "我要发一个 GET 请求" │
│ ↓ 需求:建立到 42.192.168.1:443 的连接 │
├─────────────────────────────────────────┤
│ 传输层 (TCP): "我来保障可靠传输" │
│ ↓ 工作:三次握手 (SYN → SYN-ACK → ACK) │
│ ↓ 需求:把 SYN 包发到 42.192.168.1 │
├─────────────────────────────────────────┤
│ 网络层 (IP): "我来找路由" │
│ ↓ 工作:查路由表,确定下一跳 │
│ ↓ 需求:把包发到网关的 MAC 地址 │
├─────────────────────────────────────────┤
│ 数据链路层: "我来物理传输" │
│ ↓ 工作:封装以太网帧,发送电信号 │
└─────────────────────────────────────────┘

✅ 三次握手是传输层的"工作规范"
✅ 每层接收上层需求,完成自己的工作,向下层提需求
✅ 整个过程是"封装-传递-解封装"的栈式流程
✅ 层与层之间是"黑盒协作",互不了解内部实现

1.4 每一层依赖于什么软硬件

层级依托的实体具体实现
应用层 (HTTP/Django) 软件程序 浏览器、Python 进程
传输层 (TCP) 操作系统内核 Linux/Windows 的 TCP/IP 协议栈
网络层 (IP) 操作系统内核 + 路由器 内核路由表、路由器硬件
数据链路层 (MAC) 网卡驱动 + 交换机 以太网卡、Wi-Fi 芯片
物理层 硬件设备 网线、光纤、无线电波、网卡电路

1.5 回顾流程

【请求阶段】

  • 浏览器(应用层)

    • DNS 查询(域名 → IP)
    • 构造 HTTP 请求报文
    • 操作系统封装:HTTP → TCP → IP → 以太网帧 → 比特流
  • 网络传输(TCP/IP 协议栈)

    • TCP 三次握手建立端到端连接(双向管道)
    • IP 逐跳路由(动态路径,去程回程可能不同)
    • 数据链路层逐跳传输(MAC 地址重写)
  • 服务器硬件/操作系统

    • 物理层:网卡接收电信号
    • 数据链路层:解封装以太网帧(检查 MAC)
    • 网络层:解封装 IP 包(检查目的 IP)
    • 传输层:解封装 TCP 段(重组数据流,检查端口)
  • Web 服务器(Gunicorn/uWSGI,应用层)

    • 读取 TCP 数据流,解析为 HTTP 请求字节流
    • 调用 WSGI 接口:application(environ, start_response)
  • WSGI 协议转换

    • 输入:HTTP 字节流 → 转为 Python 字典 environ
    • 传给 Django,附带 start_response 回调函数
  • Django(应用层)

    • WSGIHandler:environ → HttpRequest 对象
    • 内部处理:URL 路由 → 视图 → 模型 → 模板
    • 生成 HttpResponse 对象
    • 转为 WSGI 格式:调用 start_response(status, headers)
    • 返回可迭代对象(字节串序列)
  • 【响应阶段】
    7. WSGI 协议转换

    • 可迭代对象 → HTTP 响应字节流
    • 传给 Web 服务器
  • Web 服务器 → 操作系统

    • 写入 TCP 连接(同一连接,反向传输)
  • 网络传输(TCP/IP 协议栈)

    • TCP 可靠传输(同一连接,序列号确认)
    • IP 路由(可能不同于去程路径)
    • 数据链路层逐跳传输
  • 浏览器(应用层)

    • 操作系统解封装:比特流 → 帧 → IP 包 → TCP 段 → HTTP 数据
    • 浏览器解析 HTTP 响应
    • 渲染引擎:HTML → DOM,CSS → CSSOM,JS 执行 → 绘制页面
  • 【连接管理】

    • 连接保持(Keep-Alive):后续请求复用同一 TCP 连接
    • 连接关闭:TCP 四次挥手(双方独立关闭半连接)

    1.6 项目的其他网络通信

    大型项目中,数据库、静态文件、缓存往往部署在独立的服务器上,每次访问都涉及完整的网络通信流程。

    • 以 Nginx + WSGI/ASGI + Django 框架为例

    用户浏览器

    ▼ ① HTTP 请求(完整 TCP/IP 流程)
    Nginx 服务器(反向代理)

    ├─► ② 静态文件请求(本地或 CDN)
    │ 如果是 CDN:再次完整 TCP/IP 流程到云服务商

    └─► ③ 动态请求转发给 Django(本地 Unix Socket 或 TCP)

    ▼ ④ Django ORM 查询数据库

    ├─► ④-a 主数据库查询(TCP/IP 到数据库服务器)

    └─► ④-b Redis 缓存查询(TCP/IP 到缓存服务器)

    ▼ ⑤ 所有结果返回,层层组装响应

    • 问题:每次查询都 TCP 三次握手?太慢了!
      • 解决方案:

    ┌─────────────────────────────────────┐
    │ 连接池(Connection Pool) │
    │ │
    │ Django 启动时,预先建立 N 个 TCP 连接 │
    │ 到数据库,放入"池子"中 │
    │ │
    │ 需要查询时:从池中取连接 → 发送 SQL │
    │ 查询完成后:连接归还池中(不断开) │
    │ │
    │ 避免了频繁的 TCP 三次握手! │
    └─────────────────────────────────────┘

    2. 在服务器上(以 WhiteNoise 方案为例)

    • 初学者一般采用 WhiteNoise 中间件,而非 Nginx,并部署到 PaaS 平台
      • WSGI(Gunicorn) + WhiteNoise + Django
    • 下面介绍这套框架下服务器上有哪些进程

    2.1 Gunicorn & Worker

    ┌─────────────────────────────────────────┐
    │ Gunicorn Master 进程 │ ← 管理者,不处理请求
    │ (负责启动/管理/监控 workers) │
    │ │
    │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
    │ │ Worker 1│ │ Worker 2│ │ Worker 3│ │ ← 实际处理 HTTP 请求的进程
    │ │ (Django)│ │ (Django)│ │ (Django)│ │ 每个都有独立的 Python 解释器
    │ └─────────┘ └─────────┘ └─────────┘ │ 都加载了完整的 Django 应用
    │ │
    │ 默认:1 个 master + 1 个 worker(可配置) │
    └─────────────────────────────────────────┘

    2.2 Worker 内部

    Worker 进程内部:
    ┌─────────────────────────────────────────┐
    │ Gunicorn 的 HTTP 解析器 │
    │ (把 HTTP 字节流解析为 WSGI environ) │
    ├─────────────────────────────────────────┤
    │ WSGI 接口层 │
    │ (调用 Django 的 application 函数) │
    ├─────────────────────────────────────────┤
    │ Django 应用(完整加载) │
    │ WSGIHandler → 中间件链 │
    | → URL 路由 → 视图 → 模板 → 响应 │
    └─────────────────────────────────────────┘

    2.3 WhiteNoise 中间件的位置

    • WhiteNoise 中间件
      • 是中间件链(流水线)中的一环
      • 用于判断是否直接返回静态文件
      • 从而提高页面响应效率

    1】请求流程(带 WhiteNoise):

    用户请求 /static/style.css


    Gunicorn Worker


    WhiteNoise 中间件(Django 内部)

    ├─► 如果是静态文件:直接读取本地文件系统返回(不经过 Django 视图)

    └─► 如果是动态请求:传递给 Django URL 路由 → 视图

    2】实际进程:
    ┌─────────────────┐
    │ WSGI 管理者 │
    (Gunicorn)
    └────────┬────────┘

    ┌────┴────┐
    ▼ ▼
    ┌───────┐ ┌───────┐
    │Worker1│ │Worker2│ ← Django 进程(每个都是完整的 Django)
    │ │ │ │
    │ Django│ │ Django│ 包含:WSGIHandler → 中间件链 → URL路由 → 视图
    │ 完整栈 │ │ 完整栈 │
    └───────┘ └───────┘

    中间件不是"壳",而是"流水线检查站"
    请求方向:WSGI → M1 → M2 → M3 → 视图
    响应方向:视图 → M3 → M2 → M1 → WSGI

    2.4 当部署到 PaaS 平台

    用户请求


    ┌──────────────────┐
    │ PaaS 负载均衡器 │ ← 平台管理,分发流量到多个 dyno/container
    │ (Nginx/AWS ALB) │
    └─────────┬────────┘

    ┌─────┴─────┐
    ▼ ▼
    ┌────────┐ ┌────────┐
    │Dyno 1 │ │Dyno 2 │ ← 你的"服务实例",每个内部是:
    │ │ │ │ Gunicorn Master + N Workers + Django
    │Gunicorn│ │Gunicorn│
    │Django │ │Django │
    └────────┘ └────────┘

    3. Django 的魔法

    3.1 实际调用流程

    ┌─────────────────────────────────────────┐
    │ WSGIHandler.__call__() │
    │ (WSGI 入口) │
    │ ├─ 创建 HttpRequest │
    │ ├─ 调用 get_response(request) │
    │ │ │
    │ │ ┌─────────────────────────────┐ │
    │ │ │ 中间件 process_request() │ │
    │ │ │ (全局校验、解析等) │ │
    │ │ └─────────────────────────────┘ │
    │ │ ↓ │
    │ │ ┌─────────────────────────────┐ │
    │ │ │ URLResolver.resolve() │ │
    │ │ │ (仅路由:path → view) │ │
    │ │ │ 返回:view_func + args │ │
    │ │ └─────────────────────────────┘ │
    │ │ ↓ │
    │ │ ┌─────────────────────────────┐ │
    │ │ │ View(视图层) │ │
    │ │ │ ├─ 业务逻辑(可能需要查 model) │ │
    │ │ │ ├─ 调用 template 渲染 │ │
    │ │ │ └─ return HttpResponse │ │
    │ │ └─────────────────────────────┘ │
    │ │ ↓ │
    │ │ ┌─────────────────────────────┐ │
    │ │ │ 中间件 process_response() │ │
    │ │ │ (全局响应处理) │ │
    │ │ └─────────────────────────────┘ │
    │ │ │
    │ │ return HttpResponse │
    │ │ (回到 WSGIHandler) │
    │ ↓ │
    │ 调用 start_response(status, headers) │
    │ return response(可迭代对象) │
    └─────────────────────────────────────────┘

    返回 WSGI 服务器

    3.2 函数视图

    • 假设我们不使用 CBV(基于类的视图) 的方式
    • 我们手动实现相关的逻辑

    # views.py
    from django.http import HttpResponse
    from django.template import loader
    from .models import Article

    def home(request):
    """
    完全显式处理:
    1. 手动查询数据库
    2. 手动加载模板
    3. 手动渲染
    4. 手动构造响应
    """

    # 步骤 1:查询数据(显式 ORM 查询)
    articles = Article.objects.filter(published=True).order_by('-created_at')[:5]

    # 步骤 2:准备模板上下文(显式构造字典)
    context = {
    'title': '我的主页',
    'articles': articles,
    'user': request.user,
    }

    # 步骤 3:手动加载模板文件
    template = loader.get_template('home.html')

    # 步骤 4:手动渲染(模板 + 上下文 = HTML 字符串)
    html_string = template.render(context, request)

    # 步骤 5:手动构造 HTTP 响应
    response = HttpResponse(html_string)
    response['Cache-Control'] = 'max-age=3600' # 甚至可以手动加响应头

    return response

    3.3 基于类的视图

    • 代码样例

    # views.py
    from django.views.generic import ListView
    from .models import Article

    class HomeView(ListView):
    # 声明式配置:Django 自动推导所有行为
    model = Article # 查哪张表
    template_name = 'home.html' # 用哪个模板
    context_object_name = 'articles' # 模板中变量叫啥

    # 甚至查询条件都帮你封装好了
    queryset = Article.objects.filter(published=True).order_by('-created_at')[:5]

    # 额外上下文数据(自动合并到模板)
    def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    context['title'] = '我的主页' # 自动注入模板
    return context

    • 内部干了什么

    HomeView.as_view() 被调用

    View.dispatch(request, *args, **kwargs) # 分发 HTTP 方法

    ListView.get(request, …) # 处理 GET 请求

    ListView.get_queryset() # 获取查询集(你用 queryset 或 model 定义的)

    ListView.get_context_data() # 构造上下文(你可以覆盖添加变量)

    ListView.render_to_response() # 渲染模板

    TemplateResponse 返回

    3.4 在开发者视角

    ┌─────────────────────────────────────────┐
    │ 用户看到的"面子"(浏览器端) │
    │ ───────────────────────────────────── │
    │ HTML 结构(语义) │
    │ CSS 样式(视觉) │
    │ JavaScript 交互(行为) │
    └─────────────────────────────────────────┘
    ↑ 通过网络传输
    ┌─────────────────────────────────────────┐
    │ Django 服务器的"分层" │
    │ ───────────────────────────────────── │
    │ │
    │ 模板层(Template)← "面子"的半成品 │
    │ ├─ HTML 骨架(带 {{变量}} 和 {%标签%}) │
    │ ├─ 展示逻辑(循环、条件判断) │
    │ └─ 过滤器(日期格式化、截断文字等) │
    │ │
    │ 视图层(View)← "协调者",连接里子和面子 │
    │ ├─ 接收请求,调用模型查数据 │
    │ ├─ 准备上下文(数据打包) │
    │ └─ 选择模板,触发渲染 │
    │ │
    │ 模型层(Model)← 真正的"里子" │
    │ ├─ 数据库表结构定义 │
    │ ├─ 数据查询/创建/更新/删除逻辑 │
    │ └─ 业务规则验证(字段校验等) │
    │ │
    └─────────────────────────────────────────┘

    3.5 一些问题 & 解答

    • Q1: 假设 我在后端拿到了 用户在网页上输入的数据
      • (1)如果需要做一些逻辑处理再进行存储或展示,应该怎么做?
        • A: CBV 覆盖方法(函数重写)
      • (2)假设我需要再后端做数据处理,那么多用户同时访问,内存是否有压力?
        • A: 效率和多用户问题可控
    场景影响解决方案
    简单计算 + 单条插入 无压力 直接用
    复杂计算(如 AI 处理) 阻塞用户等待 异步任务队列(Celery)
    大量数据写入 数据库压力大 批量插入、事务优化
    多用户并发 内存/CPU 竞争 多进程/多服务器 + 负载均衡
    • Q2: 如果除了页面所展示的静态样式,我还想要做一些交互逻辑,我应该怎么办?

      • A: 用 JavaScript
    • Q3: 如果有这样一个业务

      • 实现一个“用户自定义页面”的功能
        • 也就是仅给用户提供少量的操作空间(比如只需要填写简单的一些内容),
        • 然后在后台自动按照某种“模版”来组织起这个页面
      • Django 框架下是否能够实现?
        • A: 用户自定义页面用模型存储配置 + 动态模板选择,是 Django 最擅长的 CMS 场景。

    4. 关于前端

    4.1 前端技术简介

    • 主要的技术
    技术运行位置职责类比
    HTML 浏览器 结构(有什么) 骨骼
    CSS 浏览器 样式(长什么样) 皮肤、衣服
    JavaScript 浏览器 行为(能做什么) 肌肉、神经系统
    • 为何学习前端?
    场景仅靠 Django 模板加上前端技术
    布局美观 默认样式很丑 CSS 实现现代设计系统
    响应式适配 手机上看崩溃 一套代码适配手机/平板/桌面
    交互反馈 点击后白屏等待 加载动画、即时反馈
    实时更新 必须刷新页面 JS 动态更新部分内容
    复杂组件 无法实现 图表、地图、编辑器、画板
    • 有哪些技术路线?
    方案技术栈适用场景
    Django + Tailwind CSS utility-first CSS 快速做出漂亮界面,几乎不写 CSS
    Django + HTMX HTML 属性实现交互 不用写 JS,实现局部刷新
    Django + Alpine.js 轻量 JS 框架 简单交互(下拉菜单、标签页)
    Django + Vue/React 完整前端框架 复杂单页应用(SPA)
    • 如何选择?
      • 方案1 – 快速路线:现在就用 Tailwind + HTMX 改造现有项目
      • 方案2 – 系统学习:从 HTML/CSS/JS 基础开始,循序渐进
      • 方案3 – 混合路线:先学 Tailwind 让页面变好看,再按需学 JS

    4.2 JavaScript 与 Django

    • 简介

      • JavaScript 是为了页面交互效果而写的脚本
      • 它运行在浏览器端
      • 属于静态文件
      • 和 Django 的 Python 代码是完全分离的两个世界
    • 开发方式

    方式说明存放位置
    手写原生 JS 自己写特效逻辑 static/js/effects.js
    引入第三方库 用现成的(如 GSAP、Three.js) CDN 或 static/vendor/
    • 在 Django 中的结构

    项目结构:
    myproject/
    ├── static/ ← 收集所有静态文件
    │ ├── js/
    │ │ ├── main.js ← 你的自定义脚本
    │ │ ├── animations.js ← 特效逻辑
    │ │ └── vendor/
    │ │ ├── gsap.min.js ← 第三方动画库
    │ │ └── three.js ← 3D 特效库
    │ ├── css/
    │ └── images/
    └── templates/
    └── base.html ← 引入 JS

    5. 关于 CBV 的另一个例子

    • CBV
      • 基于类的视图
      • 体现了 Django 最核心的设计哲学——“约定优于配置”
    • 现在,以上一篇博客的“创建博客”页面为例,再来体会一下 CVB 的优势。
      • CBV 的"魔法"是约定 + 可覆盖的方法钩子

    5.1 CBV 代码

    • 视图代码

    from django.views.generic.edit import CreateView
    from .models import Post

    class BlogCreateView(CreateView):
    model = Post
    template_name = "post_new.html"
    fields = ["title", "author", "body"]

    • 对应的 html 模版如下:

    <!– templates/post_new.html –>
    {% extends "base.html" %}
    {% block content %}
    <h1>New post</h1>
    <form action="" method="post">{% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="Save">
    </form>
    {% endblock content %}

    这个页面上提供了 三个可改动的部分,即标题、作者、正文。
    用户可以通过点击确认来完成创建。
    太神奇了,居然看不见任何处理请求、响应、交互逻辑的代码。

    5.2 函数视图代码

    如果我们想手搓一个类似功能的函数,我们需要在 veiws.py 里面写如下内容:

    # views.py
    from django.shortcuts import render, redirect
    from django.contrib import messages
    from django.urls import reverse
    from .models import Post

    def post_create_view(request):
    """
    完全复现 BlogCreateView 的功能
    """

    # 1. GET 请求:显示空表单
    if request.method == 'GET':
    # 手动构造表单(相当于 CreateView 的 form 实例化)
    from django.forms import ModelForm

    class PostForm(ModelForm):
    class Meta:
    model = Post
    fields = ['title', 'author', 'body']

    form = PostForm() # 空表单,没有 instance
    return render(request, 'post_new.html', {'form': form})

    # 2. POST 请求:处理表单提交
    elif request.method == 'POST':
    from django.forms import ModelForm

    class PostForm(ModelForm):
    class Meta:
    model = Post
    fields = ['title', 'author', 'body']

    # 绑定数据(相当于 CreateView 的 form_valid 之前)
    form = PostForm(request.POST)

    # 3. 验证表单(CreateView 自动调用 is_valid())
    if form.is_valid():
    # 4. 保存数据(相当于 form.save())
    post = form.save()

    # 5. 成功消息(CreateView 默认没有,但可配置)
    messages.success(request, f'文章 "{post.title}" 创建成功!')

    # 6. 重定向到详情页(CreateView 默认 get_absolute_url())
    # 如果没有 get_absolute_url,就重定向到列表页
    try:
    return redirect(post.get_absolute_url())
    except AttributeError:
    return redirect('post_list') # 假设有这个 URL name

    # 7. 验证失败:重新渲染表单,显示错误
    else:
    return render(request, 'post_new.html', {'form': form})

    # 3. 其他方法(PUT/DELETE 等):不允许
    else:
    from django.http import HttpResponseNotAllowed
    return HttpResponseNotAllowed(['GET', 'POST'])

    5.3 CBV 内部干了什么

    用户请求 GET /post/new/

    CreateView.as_view() 返回的函数被调用

    View.dispatch(request) 分发到 get() 方法

    CreateView.get()
    ├─ self.get_form_class() → 从 fields 推断 PostForm
    ├─ self.get_form(form_class) → 实例化空表单(unbound)
    ├─ self.get_context_data(form=form) → 构造上下文
    └─ self.render_to_response(context) → 渲染模板

    用户请求 POST /post/new/

    CreateView.post()
    ├─ self.get_form_class() → 同样的 PostForm
    ├─ self.get_form(form_class) → 绑定数据的表单(bound)
    ├─ form.is_valid() → 验证
    │ ↓
    │ 验证失败:form_invalid(form) → 重新渲染带错误
    │ ↓
    │ 验证成功:form_valid(form)
    │ ├─ form.save() → 创建数据库记录
    │ ├─ self.object = post(保存实例供后续使用)
    │ └─ return redirect(self.get_success_url())
    │ ├─ 优先用 self.success_url
    │ └─ 否则用 self.object.get_absolute_url()

    • 关键方法对照表
    CBV 方法函数视图对应作用
    get_form_class() 内嵌 PostForm 类 确定用哪个表单
    get_form() PostForm() / PostForm(request.POST) 实例化表单
    form.is_valid() form.is_valid() 验证数据
    form_valid() 保存 + 消息 + 重定向 成功后的处理
    form_invalid() 重新渲染模板 失败后的处理
    get_success_url() redirect(…) 成功后去哪

    6. 扩展知识

    6.1 钩子

    6.1.1 简介

    • "钩子"是软件工程中最核心的设计模式之一
    • 钩子的本质:预留的"插入点"
    • 核心思想
      • 框架/类库:定义标准流程(骨架)
      • 开发者:在特定时机插入自定义逻辑(血肉)

    6.1.2 Django 中的钩子类型

    • 类型 1:方法覆盖(Method Hook)
      • CBV 中使用这类钩子
      • 继承并“重写函数”即可
    • 类型 2:回调注册(Callback Hook)
      • 中间件使用这类钩子
      • 自定义一个可调用的类或者函数,注册到中间件流水线列表中
    • 类型 3:信号(Signal Hook)
      • 解耦的钩子:不修改类,也能响应事件
      • Django 内置信号(模型保存、请求开始等),或自定义信号使用这类钩子
      • 用 @receiver 装饰器注册监听函数,或手动 signal.connect()

    6.1.3 更多的 钩子的形态

    形态示例钩子特征
    抽象方法 NotImplementedError 方法 强制子类实现
    空方法 pass 方法 可选覆盖
    默认实现 有默认代码,但允许覆盖 提供默认行为
    回调函数 函数参数、lambda、闭包 运行时注入
    事件监听 信号、观察者模式 多对多解耦
    配置注入 依赖注入容器 运行时组装
    装饰器 @login_required 包装增强

    6.2 装饰器

    • 装饰器是 Python 中最优雅的设计模式之一
    • 本质上是一种特殊的钩子——在不修改原函数的情况下,给它"包装"额外功能。

    原始函数: 装饰后函数:
    ┌─────────┐ ┌───────────────┐
    │ 你的函数 │ → │ 装饰器逻辑 │
    │ core() │ │ (前置/后置) │
    └─────────┘ │ ┌─────────┐ │
    │ │ 你的函数 │ │
    │ │ core() │ │
    │ └─────────┘ │
    │ (后置/前置) │
    └───────────────┘

    • 基础写法举例

    def my_decorator(func):
    def wrapper():
    print("前置逻辑")
    result = func()
    print("后置逻辑")
    return result
    return wrapper

    @my_decorator # ← 等价于 say_hello = my_decorator(say_hello)
    def say_hello():
    print("Hello!")

    say_hello()

    • 被装饰的函数有参数

    def log_decorator(func):
    def wrapper(*args, **kwargs): # 接收任意参数
    print(f"调用 {func.__name__},参数:{args}, {kwargs}")
    result = func(*args, **kwargs) # 原封不动传给原函数
    print(f"{func.__name__} 返回:{result}")
    return result
    return wrapper

    @log_decorator
    def add(a, b):
    return a + b

    add(2, 3)
    # 输出:
    # 调用 add,参数:(2, 3), {}
    # add 返回:5

    • 装饰器本身带参数

    def route(url):
    """路由装饰器,收集到注册表"""
    registry = {}
    def decorator(func):
    registry[url] = func
    return func
    return decorator

    # 批量定义路由
    routes = []

    @route('/home')
    def home():
    pass
    routes.append(home)

    @route('/about')
    def about():
    pass
    routes.append(about)

    # 等价效果:多个函数共享同一个装饰器"工厂"

    • 多层装饰器:洋葱模型

    # 核心函数(洋葱心)
    @decorator_a # ← 等价于 core = decorator_a(decorator_b(core))
    @decorator_b # ← 等价于 core = decorator_b(core)
    def core_function(x):
    """最核心逻辑"""
    print(f" 【核心】执行:x = {x}")
    return x * 2

    # 执行时:a前 → b前 → core_function → b后 → a后

    赞(0)
    未经允许不得转载:171主机测评 » 【Web应用开发笔记】Django笔记7-2:从零开始理解 Django
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址