Django 6.0 从入门到实战教程(上机实操版)

Django 6.0 从入门到实战教程(上机实操版)

服务器环境 :华为云 FlexusX ecs-60a4-0001 | Ubuntu 24.04.4 LTS | Python 3.12.3 | Django 6.0.6 IP :114.116.250.214(弹性公网)/ 192.168.0.238(私有) 规格 :8vCPU | 16GiB | x2e.8u.16g | 可用区7 | 5Mbit/s BGP 实操日期:2026-06-27


目录

篇章 章节 内容
入门篇 1-5 简介 → 安装 → 创建项目 → django-admin → 项目结构
核心篇 6-11 模板 → 模型 → 表单 → 视图 → 路由 → Admin
ORM篇 12-14 单表操作 → 多表操作 → 聚合查询
进阶篇 15-19 Form组件 → Auth → Cookie/Session → 中间件 → FBV/CBV
实战篇 20-24 配置管理 → 请求生命周期 → 博客项目 → 部署 → 测试

环境架构

lua 复制代码
+----------------------------------------------------------+
|               华为云 FlexusX ecs-60a4-0001                |
|               Ubuntu 24.04.4 LTS                          |
|               8vCPU / 16GiB                               |
|                                                          |
|  +------------------+    +------------------+             |
|  | Python 3.12.3    |    | pip 24.0         |             |
|  +------------------+    +------------------+             |
|         |                        |                        |
|         v                        v                        |
|  +------------------------------------------+             |
|  |          Django 6.0.6                     |             |
|  |  +------------+  +------------------+    |             |
|  |  | sqlparse   |  | asgiref 3.11.1   |    |             |
|  |  | 0.5.5      |  |                  |    |             |
|  |  +------------+  +------------------+    |             |
|  +------------------------------------------+             |
|         |                                                |
|         v                                                |
|  +------------------------------------------+             |
|  |          SQLite 3 (内置)                  |             |
|  |          db.sqlite3                       |             |
|  +------------------------------------------+             |
+----------------------------------------------------------+

第一章 Django 简介

1.1 什么是 Django

Django 是一个高性能的 Python Web 框架,遵循 MTV(Model-Template-View) 架构模式,以 快速开发DRY(Don't Repeat Yourself) 为设计哲学。

核心特性

特性 说明
ORM 对象关系映射,无需写 SQL 即可操作数据库
Admin 内置管理后台,自动生成 CRUD 界面
模板引擎 Django Template Language (DTL),支持继承和过滤器
表单处理 Form / ModelForm,自动验证和渲染
认证系统 内置 User / Group / Permission / Session
中间件 请求/响应处理管道,可插拔式扩展
安全防护 CSRF / XSS / SQL 注入 / Clickjacking 防护
缓存框架 支持 Memcached / Redis / 数据库 / 文件系统
国际化 多语言、多时区支持

1.2 MTV 架构

sql 复制代码
                    +------------------+
                    |     Model        |  数据模型层
                    |  (ORM -> DB)     |  定义数据结构
                    +--------+---------+
                             |
                    +--------+---------+
                    |     View         |  视图层 (控制器)
                    |  (业务逻辑)       |  处理请求,调用 Model
                    +--------+---------+
                             |
                    +--------+---------+
                    |    Template      |  模板层 (视图)
                    |  (HTML 渲染)     |  生成最终 HTML
                    +------------------+

与 MVC 的对应关系

MVC Django MTV 说明
Model Model 数据模型
View Template 展示层
Controller View 业务逻辑控制器

1.3 Django 版本历史

版本 发布时间 重要特性 LTS
1.x 2005-2017 奠定基础架构 -
2.x 2017-2019 简化 URL 路由语法 2.2
3.x 2019-2021 ASGI 异步支持 3.2
4.x 2021-2023 异步视图/ORM 4.2
5.x 2023-2025 数据库计算默认值、模型字段 5.2
6.0 2025-12 移除 Python 3.10/3.11 支持 -

第二章 Django 安装

2.1 系统环境

bash 复制代码
root@ecs-60a4-0001:~# python3 --version
Python 3.12.3

root@ecs-60a4-0001:~# cat /etc/os-release | head -2
PRETTY_NAME="Ubuntu 24.04.4 LTS"
NAME="Ubuntu"

root@ecs-60a4-0001:~# nproc && free -h | head -2
8
               total        used        free      shared  buff/cache   available
Mem:            15Gi       2.1Gi        11Gi       124Mi       2.8Gi        13Gi

2.2 安装 Django

踩坑 1:Ubuntu 24.04 PEP 668 保护

Ubuntu 24.04 默认禁止 pip install 直接安装到系统环境(PEP 668),会报错:

go 复制代码
error: externally-managed-environment

解决方案 :使用 --break-system-packages 标志或创建虚拟环境。

bash 复制代码
# 方案一:直接安装(加 --break-system-packages)
pip3 install --break-system-packages django

# 方案二:使用虚拟环境(推荐)
python3 -m venv myenv
source myenv/bin/activate
pip install django

踩坑 2:pip 默认源超时

华为云服务器访问 PyPI 官方源可能超时,使用国内镜像:

bash 复制代码
# 使用清华镜像源安装
pip3 install --break-system-packages -i https://pypi.tuna.tsinghua.edu.cn/simple django

实机输出

bash 复制代码
root@ecs-60a4-0001:~# pip3 install --break-system-packages -i https://pypi.tuna.tsinghua.edu.cn/simple django
Collecting django
  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/.../Django-6.0.6-py3-none-any.whl (8.4 MB)
Collecting asgiref>=3.9.1
  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/.../asgiref-3.11.1-py3-none-any.whl (24 kB)
Collecting sqlparse>=0.5.0
  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/.../sqlparse-0.5.5-py3-none-any.whl (46 kB)
Installing collected packages: sqlparse, asgiref, django
Successfully installed asgiref-3.11.1 django-6.0.6 sqlparse-0.5.5

2.3 验证安装

python 复制代码
root@ecs-60a4-0001:~# python3 -c "import django; print(f'Django {django.get_version()}')"
Django 6.0.6

依赖版本

版本 说明
Django 6.0.6 Web 框架
asgiref 3.11.1 ASGI 规范实现
sqlparse 0.5.5 SQL 解析(用于查询分析)
Python 3.12.3 运行时

第三章 创建第一个项目

3.1 使用 django-admin 创建项目

bash 复制代码
mkdir /tmp/django_tutorial && cd /tmp/django_tutorial
django-admin startproject myproject

3.2 项目结构

scss 复制代码
myproject/
  manage.py              # 命令行管理工具 (665 bytes)
  myproject/             # 项目包目录
    __init__.py          # 空文件,标识 Python 包 (0 bytes)
    settings.py          # 项目配置 (3009 bytes)
    urls.py              # 根 URL 路由 (765 bytes)
    asgi.py              # ASGI 入口 (异步) (395 bytes)
    wsgi.py              # WSGI 入口 (同步) (395 bytes)

文件说明

文件 作用
manage.py Django 命令行工具,与 django-admin 功能相同但自动设置 DJANGO_SETTINGS_MODULE
settings.py 项目全局配置(数据库、应用、中间件等)
urls.py URL 路由配置,将 URL 映射到视图
wsgi.py WSGI 部署入口(Gunicorn / uWSGI)
asgi.py ASGI 部署入口(异步,WebSocket)

3.3 manage.py 源码解析

python 复制代码
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys

def main():
    """Run administrative tasks."""
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)

if __name__ == '__main__':
    main()

核心逻辑:

  1. 设置 DJANGO_SETTINGS_MODULE 环境变量指向项目的 settings.py
  2. 调用 execute_from_command_line 解析命令行参数并执行

3.4 创建第一个 App

bash 复制代码
cd myproject
python3 manage.py startapp blog

App 结构

bash 复制代码
blog/
  __init__.py       # Python 包标识
  admin.py          # Admin 注册配置
  apps.py           # App 配置类
  models.py         # 数据模型定义
  tests.py          # 单元测试
  views.py          # 视图函数
  migrations/       # 数据库迁移文件
    __init__.py

3.5 注册 App

编辑 myproject/settings.py,将 blog 添加到 INSTALLED_APPS

python 复制代码
INSTALLED_APPS = [
    'django.contrib.admin',          # 管理后台
    'django.contrib.auth',           # 认证系统
    'django.contrib.contenttypes',   # 内容类型框架
    'django.contrib.sessions',       # Session 支持
    'django.contrib.messages',       # 消息框架
    'django.contrib.staticfiles',    # 静态文件管理
    'blog',                          # ← 我们的 App
]

3.6 系统检查

bash 复制代码
root@ecs-60a4-0001:~# python3 manage.py check
System check identified no issues (0 silenced).

第四章 django-admin 命令

4.1 django-admin 与 manage.py

命令 说明
django-admin 全局命令,需手动设置 DJANGO_SETTINGS_MODULE
python manage.py 项目命令,自动读取当前项目的 settings.py
python -m django 等价于 django-admin

4.2 常用命令

bash 复制代码
root@ecs-60a4-0001:~# django-admin --version
6.0.6

root@ecs-60a4-0001:~# django-admin help
Type 'django-admin help <subcommand>' for help on a specific subcommand.

Available subcommands:

[django]
    check              # 系统检查
    compilemessages    # 编译国际化消息
    createcachetable   # 创建缓存表
    dbshell            # 数据库 Shell
    diffsettings       # 配置差异对比
    dumpdata           # 导出数据 (JSON)
    flush              # 清空数据库
    inspectdb          # 反向生成模型
    loaddata           # 导入数据
    makemessages       # 生成国际化消息
    makemigrations     # 生成迁移文件
    migrate            # 执行数据库迁移
    optimizemigration  # 优化迁移
    runserver          # 启动开发服务器
    sendtestemail      # 发送测试邮件
    shell              # Django 交互 Shell
    showmigrations     # 显示迁移状态
    sqlflush           # 查看 flush SQL
    sqlmigrate         # 查看迁移 SQL
    sqlsequencereset   # 重置序列 SQL
    squashmigrations   # 压缩迁移
    startapp           # 创建应用
    startproject       # 创建项目
    test               # 运行测试
    testserver         # 测试服务器

4.3 命令速查表

命令 说明 示例
startproject 创建项目 django-admin startproject myproject
startapp 创建应用 python manage.py startapp blog
runserver 启动开发服务器 python manage.py runserver 0.0.0.0:8000
makemigrations 生成迁移文件 python manage.py makemigrations
migrate 执行数据库迁移 python manage.py migrate
createsuperuser 创建超级用户 python manage.py createsuperuser
collectstatic 收集静态文件 python manage.py collectstatic
shell Django Shell python manage.py shell
dbshell 数据库 Shell python manage.py dbshell
test 运行测试 python manage.py test
check 系统检查 python manage.py check
dumpdata 导出数据 python manage.py dumpdata blog
loaddata 导入数据 python manage.py loaddata data.json
inspectdb 反向生成模型 python manage.py inspectdb
showmigrations 显示迁移状态 python manage.py showmigrations
flush 清空数据库 python manage.py flush
sqlmigrate 查看迁移 SQL python manage.py sqlmigrate blog 0001

第五章 项目结构解析

5.1 settings.py 关键配置

python 复制代码
# ===== 应用配置 =====
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',
]

# ===== 中间件配置 =====
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',       # 安全防护
    'django.contrib.sessions.middleware.SessionMiddleware', # Session 处理
    'django.middleware.common.CommonMiddleware',            # 通用处理
    'django.middleware.csrf.CsrfViewMiddleware',            # CSRF 防护
    'django.contrib.auth.middleware.AuthenticationMiddleware', # 认证
    'django.contrib.messages.middleware.MessageMiddleware', # 消息框架
    'django.middleware.clickjacking.XFrameOptionsMiddleware', # 点击劫持防护
]

# ===== URL 配置 =====
ROOT_URLCONF = 'myproject.urls'

# ===== 数据库配置 =====
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

# ===== 国际化 =====
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_TZ = True

# ===== 静态文件 =====
STATIC_URL = 'static/'

5.2 中间件说明

序号 中间件 作用
1 SecurityMiddleware HTTPS 重定向、HSTS、X-Content-Type-Options
2 SessionMiddleware Session 读取/写入(依赖 Cookie)
3 CommonMiddleware URL 规范化(末尾斜杠)、DISALLOWED_USER_AGENTS
4 CsrfViewMiddleware CSRF Token 验证
5 AuthenticationMiddleware 从 Session 读取用户,设置 request.user
6 MessageMiddleware 消息框架(一次性通知)
7 XFrameOptionsMiddleware X-Frame-Options 头(防点击劫持)

5.3 多环境配置策略

csharp 复制代码
myproject/
  settings/
    __init__.py
    base.py       # 公共配置
    dev.py        # 开发环境
    prod.py       # 生产环境
    test.py       # 测试环境
python 复制代码
# base.py (公共配置)
SECRET_KEY = 'django-insecure-...'
INSTALLED_APPS = [...]
MIDDLEWARE = [...]

# dev.py (开发)
from .base import *
DEBUG = True
ALLOWED_HOSTS = ['*']

# prod.py (生产)
from .base import *
DEBUG = False
ALLOWED_HOSTS = ['example.com']
DATABASES = {'default': {'ENGINE': 'django.db.backends.postgresql', ...}}

使用时设置环境变量:

bash 复制代码
export DJANGO_SETTINGS_MODULE=myproject.settings.prod

第六章 Django 模板

6.1 模板渲染基础

python 复制代码
from django.template import Template, Context

# 创建模板
t = Template('{{ name }}, 欢迎学习 Django!')
c = Context({'name': 'Python开发者'})
print(t.render(c))
# 输出: Python开发者, 欢迎学习 Django!

6.2 模板标签

django 复制代码
{# 循环标签 #}
{% for item in items %}
  {{ forloop.counter }}. {{ item }}
{% endfor %}

{# 条件标签 #}
{% if count > 5 %}
  数量大于5
{% else %}
  数量不超过5
{% endif %}

{# 实机输出 #}
  1. Python
  2. Django
  3. Flask
  4. FastAPI

  数量不超过5

6.3 模板继承

django 复制代码
{# base.html #}
<html>
<head><title>{% block title %}Base{% endblock %}</title></head>
<body>
  <nav>Navigation</nav>
  {% block content %}Default content{% endblock %}
  <footer>Footer</footer>
</body>
</html>

{# child.html #}
{% extends "base.html" %}
{% block title %}Home Page{% endblock %}
{% block content %}<h1>Welcome Home</h1>{% endblock %}

6.4 内置过滤器(22个实测)

过滤器 语法 输入 输出 说明
upper `{{ s upper }}` hello HELLO
lower `{{ s lower }}` HELLO hello
title `{{ s title }}` hello world Hello World
capfirst `{{ s capfirst }}` hello Hello
add `{{ n add:10 }}` 5 15
length `{{ s length }}` django 6
truncatewords `{{ s truncatewords:2 }}` one two three four one two ...
truncatechars `{{ s truncatechars:10 }}` abcdefghijklmnop abcdefghi...
default `{{ s default:'N/A' }}` (空) N/A
yesno `{{ s yesno:'是,否,未知' }}` True
yesno `{{ s yesno:'是,否,未知' }}` False
yesno `{{ s yesno:'是,否,未知' }}` None 未知
floatformat `{{ f floatformat:2 }}` 3.14159 3.14
cut `{{ s cut:' ' }}` a b c d abcd
safe `{{ s safe }}` <b>bold</b> bold
escape `{{ s escape }}` <b>bold</b> <b>bold</b>
striptags `{{ s striptags }}` <p>Hello</p> Hello
urlencode `{{ s urlencode }}` hello world hello%20world
wordcount `{{ s wordcount }}` one two three 3
date `{{ d date:'Y-m-d' }}` 2026-06-27 2026-06-27
slugify `{{ s slugify }}` Hello World Django hello-world-django
filesizeformat `{{ n filesizeformat }}` 1048576 1.0 MB

第七章 Django 模型

7.1 模型定义

python 复制代码
from django.db import models
from django.contrib.auth.models import User


class Category(models.Model):
    """文章分类"""
    name = models.CharField('分类名称', max_length=100)
    slug = models.SlugField('URL别名', max_length=100, unique=True)
    description = models.TextField('描述', blank=True)
    created_at = models.DateTimeField('创建时间', auto_now_add=True)

    class Meta:
        verbose_name = '分类'
        verbose_name_plural = '分类'
        ordering = ['-created_at']

    def __str__(self):
        return self.name


class Tag(models.Model):
    """文章标签"""
    name = models.CharField('标签名', max_length=50, unique=True)
    created_at = models.DateTimeField('创建时间', auto_now_add=True)

    def __str__(self):
        return self.name


class Post(models.Model):
    """文章"""
    STATUS_CHOICES = [
        ('draft', '草稿'),
        ('published', '已发布'),
    ]

    title = models.CharField('标题', max_length=200)
    slug = models.SlugField('URL别名', max_length=200, unique=True)
    author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='作者')
    category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, verbose_name='分类')
    tags = models.ManyToManyField(Tag, blank=True, verbose_name='标签')
    content = models.TextField('内容')
    status = models.CharField('状态', max_length=10, choices=STATUS_CHOICES, default='draft')
    view_count = models.PositiveIntegerField('浏览量', default=0)
    created_at = models.DateTimeField('创建时间', auto_now_add=True)
    updated_at = models.DateTimeField('更新时间', auto_now=True)

    class Meta:
        verbose_name = '文章'
        verbose_name_plural = '文章'
        ordering = ['-created_at']

    def __str__(self):
        return self.title


class Comment(models.Model):
    """评论"""
    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments', verbose_name='文章')
    name = models.CharField('昵称', max_length=80)
    email = models.EmailField('邮箱')
    body = models.TextField('评论内容')
    active = models.BooleanField('是否显示', default=True)
    created_at = models.DateTimeField('创建时间', auto_now_add=True)

    class Meta:
        ordering = ['-created_at']

7.2 字段类型对照表

字段类型 Python 类型 数据库类型 说明
CharField str VARCHAR 短文本,必须指定 max_length
TextField str TEXT 长文本
IntegerField int INTEGER 整数
PositiveIntegerField int INTEGER UNSIGNED 正整数
BigIntegerField int BIGINT 大整数
FloatField float FLOAT 浮点数
DecimalField Decimal DECIMAL 精确小数
BooleanField bool BOOLEAN 布尔值
DateTimeField datetime DATETIME 日期时间
DateField date DATE 日期
EmailField str VARCHAR 邮箱(验证)
URLField str VARCHAR URL(验证)
SlugField str VARCHAR URL 友好字符串
UUIDField UUID UUID UUID
ForeignKey Model INTEGER (FK) 外键(多对一)
ManyToManyField QuerySet 中间表 多对多
OneToOneField Model INTEGER (FK) 一对一
ImageField FieldFile VARCHAR 图片文件
FileField FieldFile VARCHAR 文件

7.3 字段参数

参数 说明 示例
verbose_name 人类可读名称 verbose_name='标题'
max_length 最大长度 max_length=200
null 数据库允许 NULL null=True
blank 表单允许为空 blank=True
default 默认值 default=0
unique 唯一约束 unique=True
choices 枚举选项 choices=[('draft', '草稿')]
on_delete 外键删除策略 on_delete=models.CASCADE
auto_now 每次保存更新 auto_now=True
auto_now_add 创建时设置 auto_now_add=True
related_name 反向关系名 related_name='comments'

7.4 on_delete 选项

选项 说明
CASCADE 级联删除(删除引用对象时同时删除)
PROTECT 保护(阻止删除,抛出 ProtectedError)
SET_NULL 设为 NULL(需 null=True)
SET_DEFAULT 设为默认值(需 default=...)
SET() 设为指定值或函数返回值
DO_NOTHING 不做任何操作(可能违反完整性约束)

7.5 生成迁移

bash 复制代码
root@ecs-60a4-0001:~# python3 manage.py makemigrations blog
Migrations for 'blog':
  blog/migrations/0001_initial.py
    + Create model Category
    + Create model Tag
    + Create model Post
    + Create model Comment

7.6 执行迁移

bash 复制代码
root@ecs-60a4-0001:~# python3 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  ...
  Applying blog.0001_initial... OK
  Applying sessions.0001_initial... OK

第八章 Django 视图

8.1 FBV(函数视图)

python 复制代码
from django.shortcuts import render, get_object_or_404, redirect
from django.http import HttpResponse, JsonResponse
from .models import Post, Category


def index(request):
    """首页"""
    posts = Post.objects.filter(status='published')[:5]
    return HttpResponse(f'<h1>Blog Index</h1><p>Published posts: {posts.count()}</p>')


def post_detail(request, post_id):
    """文章详情"""
    post = get_object_or_404(Post, pk=post_id)
    post.view_count += 1
    post.save()
    return HttpResponse(f'<h1>{post.title}</h1><p>Views: {post.view_count}</p>')


def search(request):
    """搜索"""
    query = request.GET.get('q', '')
    if query:
        results = Post.objects.filter(title__icontains=query)
        return JsonResponse({
            'query': query,
            'count': results.count(),
            'results': list(results.values('id', 'title'))
        })
    return JsonResponse({'error': 'No query provided'})

8.2 CBV(类视图)

python 复制代码
from django.views import View
from django.views.generic import ListView, DetailView


class PostListView(View):
    """文章列表 - 基于 View"""
    def get(self, request):
        posts = Post.objects.filter(status='published')
        data = [{'id': p.id, 'title': p.title, 'views': p.view_count} for p in posts]
        return JsonResponse({'posts': data, 'count': len(data)})

    def post(self, request):
        return JsonResponse({'error': 'Method not allowed'}, status=405)


class PostDetailView(DetailView):
    """文章详情 - 基于通用视图"""
    model = Post
    template_name = 'blog/post_detail.html'
    context_object_name = 'post'

8.3 FBV vs CBV 对比

属性 FBV CBV
定义方式 def view(request): class View(View):
HTTP 方法 if request.method == 'GET' def get(self, request)
代码复用 低(需手动提取公共逻辑) 高(继承 Mixin)
装饰器 @login_required @method_decorator(login_required)
Mixin 不适用 可组合多个 Mixin
可读性 简单直观 需要理解类视图体系
适用场景 简单逻辑 RESTful API / CRUD
Django 内置 render / redirect ListView/DetailView/CreateView

8.4 通用类视图

视图 说明
View 基础类视图,需手动实现 HTTP 方法
TemplateView 渲染模板
RedirectView 重定向
ListView 列表页(分页支持)
DetailView 详情页
CreateView 创建(表单+模型)
UpdateView 更新(表单+模型)
DeleteView 删除(确认页面)
FormView 表单处理

第九章 Django 路由

9.1 URL 配置

python 复制代码
# myproject/urls.py (项目级路由)
from django.contrib import admin
from django.urls import path, include
from django.http import HttpResponse

def home(request):
    return HttpResponse('<h1>Django Tutorial Home</h1>')

def hello_world(request, name='World'):
    return HttpResponse(f'<h2>Hello, {name}!</h2>')

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', home, name='home'),
    path('about/', about, name='about'),
    path('hello/<str:name>/', hello_world, name='hello'),
    path('blog/', include('blog.urls')),
]
python 复制代码
# blog/urls.py (App 级路由)
from django.urls import path, re_path
from . import views

app_name = 'blog'  # 命名空间

urlpatterns = [
    path('', views.index, name='index'),
    path('post/<int:post_id>/', views.post_detail, name='post_detail'),
    path('category/<slug:slug>/', views.category_list, name='category_detail'),
    path('search/', views.search, name='search'),
    path('api/posts/', views.PostListView.as_view(), name='api_posts'),
    path('api/posts/<int:pk>/', views.PostDetailView.as_view(), name='api_post_detail'),
    re_path(r'^archive/(?P<year>[0-9]{4})/$', views.archive, name='archive'),
]

9.2 路径转换器

转换器 匹配规则 示例 URL 示例值
str 任意非空字符串(不含 / /hello/Django/ Django
int 正整数 /post/42/ 42
slug ASCII 字母/数字/连字符/下划线 /category/python/ python
uuid UUID 格式 /post/550e8400-e29b/ UUID 对象
path 任意非空字符串(含 / /files/a/b/c.txt a/b/c.txt

9.3 URL 反解析

python 复制代码
from django.urls import reverse

reverse('home')                              # '/'
reverse('about')                             # '/about/'
reverse('hello', kwargs={'name': 'Django'})  # '/hello/Django/'
reverse('blog:index')                        # '/blog/'
reverse('blog:search')                       # '/blog/search/'

9.4 path() vs re_path()

特性 path() re_path()
语法 路径转换器 <int:post_id> 正则表达式 (?P<post_id>[0-9]+)
可读性
灵活性 有限(5 种内置转换器) 高(完全正则)
自定义 可注册自定义转换器 不需要
Django 版本 2.0+ 所有版本

第十章 Django Admin 管理工具

10.1 注册模型

python 复制代码
from django.contrib import admin
from .models import Category, Tag, Post, Comment


@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ('name', 'slug', 'description', 'created_at')
    prepopulated_fields = {'slug': ('name',)}
    search_fields = ('name', 'description')
    list_filter = ('created_at',)


@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ('title', 'author', 'category', 'status', 'view_count', 'created_at')
    list_filter = ('status', 'category', 'author', 'created_at')
    search_fields = ('title', 'content')
    prepopulated_fields = {'slug': ('title',)}
    date_hierarchy = 'created_at'
    ordering = ['-created_at']
    list_per_page = 20
    raw_id_fields = ('author',)
    filter_horizontal = ('tags',)


@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
    list_display = ('name', 'email', 'post', 'active', 'created_at')
    list_filter = ('active', 'created_at')
    search_fields = ('name', 'email', 'body')
    actions = ['approve_comments']

    @admin.action(description='批准评论')
    def approve_comments(self, request, queryset):
        queryset.update(active=True)

10.2 Admin 配置属性

属性 说明
list_display 列表页显示的字段
list_filter 侧边栏过滤器
search_fields 搜索框可搜索的字段
prepopulated_fields 自动填充字段(如 slug 由 title 填充)
date_hierarchy 按日期分层导航
ordering 默认排序
list_per_page 每页显示数量
raw_id_fields 外键使用 ID 输入框
filter_horizontal 多对多字段水平选择器
list_editable 列表页可直接编辑的字段
readonly_fields 只读字段
fieldsets 字段分组布局
actions 批量操作
empty_value_display 空值显示文本

10.3 已注册模型

rust 复制代码
auth.group    -> GroupAdmin
auth.user     -> UserAdmin
blog.category -> CategoryAdmin
blog.tag      -> TagAdmin
blog.post     -> PostAdmin
blog.comment  -> CommentAdmin

第十一章 ORM 单表操作

11.1 创建测试数据

python 复制代码
# 创建用户
admin_user = User.objects.create_superuser('admin', 'admin@example.com', 'admin12345')
alice = User.objects.create_user('alice', 'alice@example.com', 'alice123')
bob = User.objects.create_user('bob', 'bob@example.com', 'bob123')

# 创建分类
Category.objects.create(name='Python', slug='python', description='Python 编程语言')
Category.objects.create(name='Django', slug='django', description='Django Web 框架')
Category.objects.create(name='Flask', slug='flask', description='Flask 轻量级框架')
Category.objects.create(name='Docker', slug='docker', description='容器化技术')
Category.objects.create(name='Linux', slug='linux', description='Linux 系统管理')

# 创建标签
for tag in ['入门', '进阶', '实战', '面试', '性能优化', '安全', '部署', '测试']:
    Tag.objects.create(name=tag)

# 创建文章
Post.objects.create(title='Python3 基础教程', slug='python3-basic', author=alice,
                    category=cat_map['python'], status='published', view_count=150)
# ... 共 12 篇文章

11.2 CRUD 操作

python 复制代码
# Create
new_post = Post.objects.create(
    title='测试文章', slug='test-post', author=admin_user,
    category=cat_map['python'], content='测试内容', status='draft'
)
# 输出: Create: 新建文章 ID=13, title='测试文章'

# Read
Post.objects.count()                              # 13 篇文章
Post.objects.get(slug='python3-basic').title      # 'Python3 基础教程'
Post.objects.filter(status='published').count()   # 10 篇已发布
Post.objects.filter(status='draft').count()       # 3 篇草稿

# Update
new_post.title = '测试文章(已更新)'
new_post.status = 'published'
new_post.save()

# Delete
new_post.delete()
# 输出: Delete: 剩余 12 篇文章

11.3 字段查找(Field Lookups)

查找类型 查询条件 结果 说明
exact title__exact='Django 6.0 新特性' 1 精确匹配
iexact title__iexact='django 6.0 新特性' 1 不区分大小写精确匹配
contains title__contains='Django' 4 包含(区分大小写)
icontains title__icontains='django' 4 包含(不区分大小写)
startswith title__startswith='Python' 3 以...开头
endswith title__endswith='实战' 1 以...结尾
gt view_count__gt=300 6 大于
gte view_count__gte=300 6 大于等于
lt view_count__lt=100 2 小于
in status__in=['draft'] 2 在列表中
range view_count__range=(200, 500) 6 范围

11.4 Q 对象(复杂查询)

python 复制代码
from django.db.models import Q

# OR 查询
Post.objects.filter(Q(title__contains='Django') | Q(title__contains='Flask')).count()
# 输出: 5 篇

# AND 查询
Post.objects.filter(Q(title__contains='Python') & Q(status='published')).count()
# 输出: 3 篇

# NOT 查询
Post.objects.filter(~Q(status='draft')).count()
# 输出: 10 篇非草稿

11.5 F 对象(字段间运算)

python 复制代码
from django.db.models import F

# 使用 F 对象在数据库层面更新(避免竞态条件)
Post.objects.filter(slug='python3-basic').update(view_count=F('view_count') + 10)
# 更新前: 150
# 更新后: 160

11.6 排序、去重

python 复制代码
# 排序
Post.objects.order_by('-view_count')[:3]
# [('Python 异步编程详解', 680), ('Flask vs Django 对比', 580), ('Python 装饰器原理与实践', 430)]

# 排除
Post.objects.exclude(status='draft').count()  # 10 篇非草稿

# 去重
Post.objects.values_list('category', flat=True).distinct().count()  # 5 个分类

第十二章 ORM 多表操作

12.1 外键关联查询

python 复制代码
# 正向查询
cat = Category.objects.get(slug='django')
django_posts = cat.post_set.all()
# 输出: Category 'Django' 下有 3 篇文章
#   - Django REST Framework 入门 (作者: bob)
#   - Django ORM 深入解析 (作者: alice)
#   - Django 6.0 新特性 (作者: alice)

# 反向查询
user = User.objects.get(username='alice')
user_posts = user.post_set.all()
# 输出: alice 的文章: 6 篇
# 输出: alice 的已发布文章: 5 篇

12.2 跨表查询(双下划线)

python 复制代码
# Post -> Category
Post.objects.filter(category__name__contains='Docker').count()
# 输出: 2 篇

# Post -> User
Post.objects.filter(author__username='alice').count()
# 输出: 6 篇

12.3 查询优化

python 复制代码
# select_related (外键 JOIN 优化 - 单次查询)
posts = Post.objects.select_related('author', 'category').filter(status='published')[:3]
for p in posts:
    print(f"  {p.title} | 作者: {p.author.username} | 分类: {p.category.name}")
# 输出:
#   Python 装饰器原理与实践 | 作者: alice | 分类: Python
#   Linux 系统监控方案 | 作者: bob | 分类: Linux
#   Docker Compose 多容器编排 | 作者: alice | 分类: Docker

# prefetch_related (多对多优化 - 两次查询)
posts = Post.objects.prefetch_related('tags').filter(status='published')[:3]
for p in posts:
    tag_names = ', '.join([t.name for t in p.tags.all()])
    print(f"  {p.title} | 标签: {tag_names}")
# 输出:
#   Python 装饰器原理与实践 | 标签: 进阶, 面试
#   Linux 系统监控方案 | 标签: 实战, 性能优化
#   Docker Compose 多容器编排 | 标签: 实战, 部署

select_related vs prefetch_related

特性 select_related prefetch_related
适用关系 外键 / 一对一 多对多 / 反向外键
SQL 策略 JOIN(单次查询) 子查询(两次查询)
数据库负载 单次重查询 两次轻查询
适用场景 关联对象少 关联对象多

12.4 多对多操作

python 复制代码
post = Post.objects.get(slug='python3-basic')
print(f"当前标签: {[t.name for t in post.tags.all()]}")
# 输出: ['入门', '面试']

# 添加标签
new_tag = Tag.objects.create(name='新标签')
post.tags.add(new_tag)
print(f"添加后: {[t.name for t in post.tags.all()]}")
# 输出: ['入门', '面试', '新标签']

# 移除标签
post.tags.remove(new_tag)
print(f"移除后: {[t.name for t in post.tags.all()]}")
# 输出: ['入门', '面试']

第十三章 ORM 聚合查询

13.1 aggregate(聚合)

python 复制代码
from django.db.models import Count, Sum, Avg, Max, Min

agg = Post.objects.aggregate(
    total=Count('id'),
    published=Count('id', filter=Q(status='published')),
    draft=Count('id', filter=Q(status='draft')),
    total_views=Sum('view_count'),
    avg_views=Avg('view_count'),
    max_views=Max('view_count'),
    min_views=Min('view_count'),
)

实机输出

yaml 复制代码
total: 12
published: 10
draft: 2
total_views: 3700
avg_views: 308.33
max_views: 680
min_views: 30

13.2 annotate(分组聚合)

按作者分组

yaml 复制代码
alice      | 文章数: 6 | 总浏览: 1520
bob        | 文章数: 6 | 总浏览: 2180

按分类分组

yaml 复制代码
Django     | 文章数: 3 | 总浏览: 400
Docker     | 文章数: 2 | 总浏览: 770
Flask      | 文章数: 2 | 总浏览: 770
Linux      | 文章数: 2 | 总浏览: 490
Python     | 文章数: 3 | 总浏览: 1270

按标签分组

复制代码
入门         | 文章数: 4
进阶         | 文章数: 6
实战         | 文章数: 6
面试         | 文章数: 3
性能优化       | 文章数: 3
安全         | 文章数: 0
部署         | 文章数: 2
测试         | 文章数: 0

按状态分组

复制代码
draft      | 数量: 2 | 平均浏览: 40
published  | 数量: 10 | 平均浏览: 362

13.3 aggregate vs annotate

特性 aggregate annotate
返回值 字典(聚合值) QuerySet(每行附加聚合值)
用途 整体统计 分组统计
示例 全站总浏览量 每个作者的总浏览量
SQL SELECT SUM(views) FROM post SELECT author_id, SUM(views) FROM post GROUP BY author_id

第十四章 Django Form 组件

14.1 Form 定义

python 复制代码
from django import forms

class PostForm(forms.Form):
    title = forms.CharField(label='标题', max_length=200, min_length=3,
                            widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': '请输入标题'}))
    content = forms.CharField(label='内容', widget=forms.Textarea(attrs={'rows': 5}))
    status = forms.ChoiceField(label='状态', choices=[('draft', '草稿'), ('published', '已发布')])
    view_count = forms.IntegerField(label='浏览量', min_value=0, initial=0)
    email = forms.EmailField(label='联系邮箱')
    tags = forms.CharField(label='标签', required=False, help_text='多个标签用逗号分隔')

14.2 表单字段

字段名 类型 label required
title CharField 标题 True
content CharField 内容 True
status ChoiceField 状态 True
view_count IntegerField 浏览量 True
email EmailField 联系邮箱 True
tags CharField 标签 False

14.3 表单验证

有效数据验证

python 复制代码
valid_data = {
    'title': 'Django Form 验证实例',
    'content': '这是表单验证的测试内容,足够长。',
    'status': 'published',
    'view_count': 100,
    'email': 'test@example.com',
    'tags': 'Django, Form, 验证'
}
form = PostForm(valid_data)
form.is_valid()       # True
form.cleaned_data     # {'title': 'Django Form 验证实例', 'content': '...', ...}

无效数据验证

python 复制代码
invalid_data = {
    'title': 'AB',          # 太短 (min_length=3)
    'content': '',          # 必填
    'status': 'invalid',    # 非法选项
    'view_count': -1,       # 小于 min_value=0
    'email': 'not-an-email' # 邮箱格式错误
}
form = PostForm(invalid_data)
form.is_valid()       # False
form.errors
# {
#   "title": ["Ensure this value has at least 3 characters (it has 2)."],
#   "content": ["This field is required."],
#   "status": ["Select a valid choice. invalid is not one of the available choices."],
#   "view_count": ["Ensure this value is greater than or equal to 0."],
#   "email": ["Enter a valid email address."]
# }

14.4 ModelForm

python 复制代码
from django.forms import ModelForm

class PostModelForm(ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'slug', 'content', 'status', 'category']
        labels = {
            'title': '文章标题',
            'slug': 'URL别名',
            'content': '正文',
            'status': '状态',
            'category': '分类',
        }

ModelForm 字段

字段 表单类型
title CharField
slug SlugField
content CharField
status TypedChoiceField
category ModelChoiceField

第十五章 Django Auth 认证系统

15.1 用户管理

ini 复制代码
总用户数: 3
  admin      | staff=True  | superuser=True  | active=True
  alice      | staff=False | superuser=False | active=True
  bob        | staff=False | superuser=False | active=True

15.2 认证

python 复制代码
from django.contrib.auth import authenticate

user = authenticate(username='admin', password='admin12345')
# 输出: admin

user = authenticate(username='admin', password='wrong')
# 输出: None

15.3 权限系统

sql 复制代码
总权限数: 40

Blog app 权限:
  add_category              | Can add 分类
  change_category           | Can change 分类
  delete_category           | Can delete 分类
  view_category             | Can view 分类
  add_post                  | Can add 文章
  change_post               | Can change 文章
  delete_post               | Can delete 文章
  view_post                 | Can view 文章
  ...

15.4 用户组与权限

python 复制代码
# 创建用户组
editors_group = Group.objects.create(name='编辑组')

# 添加权限到组
perm_add = Permission.objects.get(codename='add_post')
perm_del = Permission.objects.get(codename='delete_post')
editors_group.permissions.add(perm_add, perm_del)
# 编辑组权限: ['add_post', 'delete_post']

# 用户加入组
bob.groups.add(editors_group)
# bob 加入编辑组后权限: {'blog.add_post', 'blog.delete_post'}

15.5 密码管理

python 复制代码
# 密码哈希算法
alice.password.split('$')[0]  # 'pbkdf2_sha256'

# 检查密码
alice.check_password('alice123')     # True/False

# 设置密码
alice.set_password('newpassword123')
alice.save()
alice.check_password('newpassword123')  # True

15.6 模拟登录

python 复制代码
# 模拟登录流程
request.session['_auth_user_id'] = str(user.pk)
request.session['_auth_user_backend'] = 'django.contrib.auth.backends.ModelBackend'
request.session.save()
# 输出: 登录成功: admin
# 输出: is_authenticated: True

# 登出
request.session.flush()
# 输出: 登出后 session 已清空

16.1 Session 配置

配置项
Session 引擎 django.contrib.sessions.backends.db
Session Cookie 名 sessionid
Session 有效期 1209600 秒(14 天)

16.2 Session 操作

python 复制代码
from django.contrib.sessions.backends.db import SessionStore

# 创建 session
request.session = SessionStore()
request.session['user_id'] = 42
request.session['preferences'] = {'theme': 'dark', 'lang': 'zh'}
request.session['cart'] = ['item1', 'item2', 'item3']
request.session.save()

# Session Key: orm3m8bkagwxy43n28p5gek95jsh4neu

# 读取 session
request2.session = SessionStore(session_key=request.session.session_key)
request2.session.get('user_id')       # 42
request2.session.get('preferences')   # {'theme': 'dark', 'lang': 'zh'}
request2.session.get('cart')          # ['item1', 'item2', 'item3']
python 复制代码
from django.http import HttpResponse

response = HttpResponse("Hello")

# 设置 Cookie
response.set_cookie('username', 'django_user', max_age=3600)
response.set_cookie('theme', 'dark', max_age=86400, httponly=True, samesite='Lax')
response.set_signed_cookie('secret', 'sensitive_data')

# 读取 Cookie
request.COOKIES.get('username')  # 'django_user'
属性 Cookie Session
存储位置 客户端浏览器 服务器端
安全性 低(可被篡改) 高(客户端仅存 session ID)
大小限制 4KB 无限制(取决于服务器)
生命周期 可设置 max_age/expires 可设置 SESSION_COOKIE_AGE
数据类型 字符串 任意可序列化对象
API response.set_cookie / request.COOKIES request.session['key'] = value

第十七章 中间件

17.1 中间件执行顺序

rust 复制代码
请求 -> SecurityMiddleware -> SessionMiddleware -> CommonMiddleware
     -> CsrfViewMiddleware -> AuthenticationMiddleware -> MessageMiddleware
     -> XFrameOptionsMiddleware -> [自定义中间件] -> 视图函数

响应 <- SecurityMiddleware <- SessionMiddleware <- CommonMiddleware
     <- CsrfViewMiddleware <- AuthenticationMiddleware <- MessageMiddleware
     <- XFrameOptionsMiddleware <- [自定义中间件] <- 视图函数

17.2 自定义中间件

python 复制代码
import time

class TimingMiddleware:
    """请求计时中间件"""
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        # 请求前处理
        start_time = time.time()
        request.start_time = start_time

        response = self.get_response(request)

        # 请求后处理
        duration = time.time() - start_time
        response['X-Request-Duration'] = f'{duration:.4f}s'
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print(f'[TimingMiddleware] 视图函数: {view_func.__name__}')
        return None

    def process_exception(self, request, exception):
        print(f'[TimingMiddleware] 异常: {exception}')
        return None


class RequestLogMiddleware:
    """请求日志中间件"""
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        method = request.method
        path = request.path
        print(f'[RequestLog] {method} {path}')

        response = self.get_response(request)

        status = response.status_code
        print(f'[RequestLog] {method} {path} -> {status}')
        return response

17.3 中间件钩子方法

方法 说明
__init__(get_response) 初始化,接收 get_response 回调
__call__(request) 处理每个请求,调用视图前后的逻辑
process_view(request, view_func, ...) 视图函数执行前调用
process_exception(request, exception) 视图抛出异常时调用
process_template_response(request, response) 模板响应处理

第十八章 FBV 与 CBV 实测

18.1 功能测试结果

使用 Django Test Client 对所有路由进行了测试:

测试项 URL 方法 状态码 结果
首页 / GET 200 <h1>Django Tutorial Home</h1>
About /about/ GET 200 <h2>About Page</h2>
Hello /hello/Django/ GET 200 <h2>Hello, Django!</h2>
博客列表 /blog/ GET 200 Published posts: 5
文章详情 /blog/post/12/ GET 200 Views: 31
分类页 /blog/category/linux/ GET 200 Posts: 2
搜索 /blog/search/?q=Python GET 200 count=3
API 列表 (CBV) /blog/api/posts/ GET 200 count=10
API POST (CBV) /blog/api/posts/ POST 405 Method not allowed
归档 /blog/archive/2026/ GET 200 Posts: 12
不存在 /blog/post/9999/ GET 404 Not Found

18.2 搜索 API 返回示例

json 复制代码
{
  "query": "Python",
  "count": 3,
  "results": [
    {"id": 11, "title": "Python 装饰器原理与实践"},
    {"id": 6, "title": "Python 异步编程详解"},
    {"id": 1, "title": "Python3 基础教程"}
  ]
}

18.3 API 列表 (CBV) 返回示例

json 复制代码
{
  "posts": [
    {"id": 11, "title": "Python 装饰器原理与实践", "views": 430},
    {"id": 10, "title": "Linux 系统监控方案", "views": 280},
    {"id": 9, "title": "Docker Compose 多容器编排", "views": 350}
  ],
  "count": 10
}

第十九章 请求生命周期

sql 复制代码
用户请求
    |
    v
+-------------------+
| Web 服务器        |  Nginx / Apache
| (HTTP -> WSGI)   |
+-------------------+
    |
    v
+-------------------+
| WSGI 服务器       |  Gunicorn / uWSGI
| (WSGI -> Django) |
+-------------------+
    |
    v
+-------------------+
| 中间件 (请求阶段)  |  SecurityMiddleware
|                   |  SessionMiddleware
|                   |  CommonMiddleware
|                   |  CsrfViewMiddleware
|                   |  AuthenticationMiddleware
|                   |  MessageMiddleware
+-------------------+
    |
    v
+-------------------+
| URL 路由          |  ROOT_URLCONF -> urls.py
| (URL -> View)    |  path('blog/', include('blog.urls'))
+-------------------+
    |
    v
+-------------------+
| 视图函数          |  FBV: def view(request)
| (处理业务逻辑)     |  CBV: View.dispatch() -> get()/post()
+-------------------+
    |
    v
+-------------------+
| 模型层            |  ORM -> SQL -> Database
| (数据库操作)       |  Post.objects.filter(...)
+-------------------+
    |
    v
+-------------------+
| 模板渲染          |  render(request, 'template.html', context)
| (HTML 生成)       |  DTL -> HTML
+-------------------+
    |
    v
+-------------------+
| 中间件 (响应阶段)  |  XFrameOptionsMiddleware
|                   |  MessageMiddleware
|                   |  ...
+-------------------+
    |
    v
+-------------------+
| HTTP 响应         |  HttpResponse / JsonResponse
+-------------------+
    |
    v
用户浏览器

第二十章 实战博客项目

20.1 数据统计

数据项 数量
用户 3
分类 5
标签 8
文章 12
已发布 10
草稿 2
评论 4
显示评论 3
隐藏评论 1
总浏览量 3702

20.2 项目结构

csharp 复制代码
myproject/
  manage.py
  myproject/
    settings.py
    urls.py
  blog/
    models.py       # Category, Tag, Post, Comment
    views.py        # FBV + CBV
    urls.py
    admin.py
    forms.py        # PostSearchForm, CommentForm
    middleware.py    # TimingMiddleware, RequestLogMiddleware
    tests.py        # 12 个单元测试
    templates/
      blog/
        base.html
        post_list.html
        post_detail.html
        search.html
    static/
      css/style.css

20.3 forms.py

python 复制代码
from django import forms
from .models import Post, Comment


class PostSearchForm(forms.Form):
    q = forms.CharField(label='搜索', max_length=200, required=False,
                        widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': '搜索文章...'}))


class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ['name', 'email', 'body']
        widgets = {
            'name': forms.TextInput(attrs={'class': 'form-control'}),
            'email': forms.EmailInput(attrs={'class': 'form-control'}),
            'body': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
        }

第二十一章 Nginx + uWSGI 部署

21.1 部署架构

lua 复制代码
                    +-------------------+
                    |     互联网用户      |
                    +---------+---------+
                              |
                              v
                    +---------+---------+
                    |    Nginx (80)     |
                    |  反向代理 + 静态   |
                    +---------+---------+
                              |
                      +-------+-------+
                      |               |
                      v               v
              +-------+---+   +-------+---+
              | uWSGI:8001|   | uWSGI:8002|  (多进程)
              +-------+---+   +-------+---+
                      |               |
                      +-------+-------+
                              |
                              v
                    +---------+---------+
                    |  Django (WSGI)   |
                    +---------+---------+
                              |
                              v
                    +---------+---------+
                    |   SQLite / PG    |
                    +-------------------+

21.2 Nginx 配置

nginx 复制代码
# /etc/nginx/sites-available/django_blog
server {
    listen 80;
    server_name example.com;
    charset utf-8;

    # 静态文件
    location /static/ {
        alias /opt/django_blog/staticfiles/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # 媒体文件
    location /media/ {
        alias /opt/django_blog/media/;
        expires 7d;
    }

    # 反向代理到 uWSGI
    location / {
        uwsgi_pass  unix:///tmp/django_blog.sock;
        include     /etc/nginx/uwsgi_params;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;
    }
}

21.3 uWSGI 配置

ini 复制代码
# /opt/django_blog/uwsgi.ini
[uwsgi]
chdir = /opt/django_blog
module = myproject.wsgi:application
processes = 4
threads = 2
socket = /tmp/django_blog.sock
chmod-socket = 664
vacuum = true
daemonize = /var/log/uwsgi/django_blog.log
pidfile = /tmp/django_blog.pid
home = /opt/venv
py-autoreload = 1
buffer-size = 65536

21.4 部署命令

bash 复制代码
# 1. 收集静态文件
python3 manage.py collectstatic --noinput

# 2. 迁移数据库
python3 manage.py migrate

# 3. 启动 uWSGI
uwsgi --ini /opt/django_blog/uwsgi.ini

# 4. 重启 Nginx
nginx -t && systemctl restart nginx

# 5. 管理命令
uwsgi --reload /tmp/django_blog.pid   # 重载
uwsgi --stop /tmp/django_blog.pid     # 停止

21.5 Gunicorn 部署(替代方案)

bash 复制代码
# 安装
pip install gunicorn

# 启动(生产模式)
gunicorn myproject.wsgi:application \
    --bind 127.0.0.1:8000 \
    --workers 4 \
    --threads 2 \
    --timeout 120 \
    --access-logfile /var/log/gunicorn/access.log \
    --error-logfile /var/log/gunicorn/error.log \
    --pid /tmp/gunicorn.pid \
    --daemon

21.6 Docker 部署

dockerfile 复制代码
# Dockerfile
FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

RUN python manage.py collectstatic --noinput
RUN python manage.py migrate

EXPOSE 8000

CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "4"]
yaml 复制代码
# docker-compose.yml
version: '3.8'

services:
  web:
    build: .
    ports:
      - "8000:8000"
    volumes:
      - .:/app
    environment:
      - DEBUG=0
    depends_on:
      - db

  db:
    image: postgres:16
    environment:
      POSTGRES_DB: django_blog
      POSTGRES_USER: django
      POSTGRES_PASSWORD: secret
    volumes:
      - pgdata:/var/lib/postgresql/data

  nginx:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
      - ./staticfiles:/var/www/static
    depends_on:
      - web

volumes:
  pgdata:

第二十二章 单元测试

22.1 测试代码

python 复制代码
from django.test import TestCase, Client
from django.contrib.auth.models import User
from .models import Category, Tag, Post, Comment
from .forms import PostSearchForm


class ModelTest(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.user = User.objects.create_user('testuser', 'test@test.com', 'password123')
        cls.category = Category.objects.create(name='Test', slug='test')
        cls.tag = Tag.objects.create(name='test-tag')
        cls.post = Post.objects.create(
            title='Test Post', slug='test-post', author=cls.user,
            category=cls.category, content='Test content', status='published'
        )
        cls.post.tags.add(cls.tag)

    def test_category_str(self):
        self.assertEqual(str(self.category), 'Test')

    def test_post_str(self):
        self.assertEqual(str(self.post), 'Test Post')

    def test_post_default_status(self):
        new_post = Post.objects.create(
            title='Draft', slug='draft', author=self.user, content='content'
        )
        self.assertEqual(new_post.status, 'draft')

    def test_post_view_count_default(self):
        self.assertEqual(self.post.view_count, 0)


class ViewTest(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.user = User.objects.create_user('testuser', 'test@test.com', 'password123')
        cls.category = Category.objects.create(name='Test', slug='test')
        cls.post = Post.objects.create(
            title='Test Post', slug='test-post', author=cls.user,
            category=cls.category, content='Test content', status='published'
        )

    def test_home_page(self):
        response = self.client.get('/')
        self.assertEqual(response.status_code, 200)

    def test_blog_index(self):
        response = self.client.get('/blog/')
        self.assertEqual(response.status_code, 200)

    def test_post_detail(self):
        response = self.client.get(f'/blog/post/{self.post.id}/')
        self.assertEqual(response.status_code, 200)

    def test_post_not_found(self):
        response = self.client.get('/blog/post/9999/')
        self.assertEqual(response.status_code, 404)

    def test_search(self):
        response = self.client.get('/blog/search/?q=Test')
        self.assertEqual(response.status_code, 200)

    def test_api_posts(self):
        response = self.client.get('/blog/api/posts/')
        self.assertEqual(response.status_code, 200)


class FormTest(TestCase):
    def test_search_form_valid(self):
        form = PostSearchForm({'q': 'Django'})
        self.assertTrue(form.is_valid())

    def test_search_form_empty(self):
        form = PostSearchForm({'q': ''})
        self.assertTrue(form.is_valid())  # q is optional

22.2 测试结果

bash 复制代码
root@ecs-60a4-0001:~# python3 manage.py test blog -v 2
Found 12 test(s).
Creating test database for alias 'default'...

test_search_form_empty (blog.tests.FormTest) ... ok
test_search_form_valid (blog.tests.FormTest) ... ok
test_category_str (blog.tests.ModelTest) ... ok
test_post_default_status (blog.tests.ModelTest) ... ok
test_post_view_count_default (blog.tests.ModelTest) ... ok
test_post_str (blog.tests.ModelTest) ... ok
test_api_posts (blog.tests.ViewTest) ... ok
test_blog_index (blog.tests.ViewTest) ... ok
test_home_page (blog.tests.ViewTest) ... ok
test_post_detail (blog.tests.ViewTest) ... ok
test_post_not_found (blog.tests.ViewTest) ... ok
test_search (blog.tests.ViewTest) ... ok

Ran 12 tests in 0.234s
OK
Destroying test database for alias 'default'...

12 个测试全部通过!

测试类 测试数 状态
ModelTest 4 全部通过
ViewTest 6 全部通过
FormTest 2 全部通过
合计 12 全部通过

第二十三章 配置管理

23.1 settings.py 关键配置项

配置项 说明
SECRET_KEY (保密) 密钥(生产环境必须保密)
DEBUG True 调试模式
ALLOWED_HOSTS ['*'] 允许的主机
INSTALLED_APPS 7 个 已安装应用
MIDDLEWARE 7 个 中间件
ROOT_URLCONF myproject.urls 根 URL 配置
DATABASES SQLite 数据库引擎
LANGUAGE_CODE en-us 语言
TIME_ZONE UTC 时区
USE_TZ True 使用时区
STATIC_URL /static/ 静态文件 URL
SESSION_ENGINE db Session 引擎
SESSION_COOKIE_AGE 14 天 Session 有效期
CSRF_COOKIE_NAME csrftoken CSRF Cookie
DEFAULT_AUTO_FIELD BigAutoField 默认主键字段

23.2 生产环境配置清单

python 复制代码
# settings/prod.py
from .base import *

DEBUG = False
ALLOWED_HOSTS = ['example.com', 'www.example.com']

# 数据库
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'django_blog',
        'USER': 'django',
        'PASSWORD': os.environ.get('DB_PASSWORD'),
        'HOST': 'localhost',
        'PORT': '5432',
    }
}

# 静态文件
STATIC_ROOT = '/opt/django_blog/staticfiles/'
STATIC_URL = '/static/'

# 安全
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True

# 日志
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'file': {
            'level': 'WARNING',
            'class': 'logging.FileHandler',
            'filename': '/var/log/django/django.log',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['file'],
            'level': 'WARNING',
            'propagate': True,
        },
    },
}

踩坑记录

踩坑 1:Ubuntu 24.04 PEP 668 保护

现象pip3 install django 报错 externally-managed-environment

原因:Ubuntu 24.04 遵循 PEP 668,禁止 pip 直接安装到系统环境

解决 :加 --break-system-packages 标志

bash 复制代码
pip3 install --break-system-packages django

踩坑 2:pip 下载超时

现象:从 PyPI 官方源下载 Django 8.4MB 超时

原因:华为云服务器访问 files.pythonhosted.org 延迟高

解决:使用清华镜像源

bash 复制代码
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple django

踩坑 3:ALLOWED_HOSTS 限制

现象:使用 Django Test Client 请求返回 400 Bad Request

原因ALLOWED_HOSTS 默认为 ['example.com'],不包含 testserver

解决 :在 settings.py 中设置 ALLOWED_HOSTS = ['*'] 或在测试中动态设置

python 复制代码
from django.conf import settings
settings.ALLOWED_HOSTS = ['*']

踩坑 4:用户权限缓存

现象 :给用户添加权限后 has_perm() 仍返回 False

原因:Django 会缓存用户权限,修改后需要重新从数据库获取

解决:重新获取用户对象

python 复制代码
alice.user_permissions.add(perm)
alice.save()
alice = User.objects.get(username='alice')  # 重新获取刷新缓存
alice.has_perm('blog.add_post')  # True

踩坑 5:RequestFactory session 不完整

现象 :使用 request.session = {} 调用 login() 报错 AttributeError: 'dict' object has no attribute 'cycle_key'

原因login() 需要 SessionStore 对象,不能是普通 dict

解决 :使用 SessionStore 或手动设置 session 字段

python 复制代码
from django.contrib.sessions.backends.db import SessionStore
request.session = SessionStore()
# 或手动设置
request.session['_auth_user_id'] = str(user.pk)

踩坑 6:blog URL namespace 未注册

现象reverse('blog:index') 报错 NoReverseMatch: 'blog' is not a registered namespace

原因 :blog/urls.py 中缺少 app_name = 'blog'

解决:在 blog/urls.py 顶部添加

python 复制代码
app_name = 'blog'  # 命名空间

总结

本教程在华为云 FlexusX ecs-60a4-0001(Ubuntu 24.04 / Python 3.12.3 / Django 6.0.6)上完成了从入门到实战的全部实验:

篇章 实验 实机验证
入门篇 安装、创建项目、django-admin、项目结构 4 个实验
核心篇 模板(22个过滤器)、模型(4个模型)、表单、视图、路由、Admin 6 个实验
ORM篇 单表CRUD、多表查询、聚合统计 3 个实验
进阶篇 Form组件、Auth、Cookie/Session、中间件、FBV/CBV 5 个实验
实战篇 博客项目、Nginx+uWSGI部署、Docker部署、单元测试 4 个实验
合计 22 个实验 12 个测试全部通过

技术栈:Django 6.0.6 + Python 3.12.3 + SQLite + DTL + ORM + Admin + Auth + Forms + Middleware + Nginx + uWSGI/Gunicorn + Docker

相关推荐
ZhengEnCi2 小时前
J7A-高级Java工程师面试三道灵魂拷问-深度广度与工程素养的终极检验
java·后端
爱勇宝4 小时前
小红花成长新版:模板来了,鼓励也更容易开始
前端·后端·程序员
用户47949283569154 小时前
翻完 lark-cli 的 17 万行 Go 代码,我学到了什么
后端·openai
卷无止境4 小时前
Eigen 库如何借助 OpenMP 加速计算
c++·后端
羑悻4 小时前
别再只接个 API 了!我用 EdgeOne Makers 手搓了一个“懂业务”的官网售前 AI
后端
卷无止境5 小时前
OpenMPI、MPICH 与 OpenMP:关系、核心概念与架构全解
c++·后端
程序员威哥5 小时前
零基础玩转西门子PLC:C#手撕S7协议,打造工业数据采集神器
后端
用户742837256335 小时前
【Ambari Plus】Step9—AmbariServer 初始化
后端
wuxinzhe76cmd5 小时前
JVM 垃圾回收基础:从 STW 到分代收集(附 G1/ZGC 导读)
后端