1 介绍

1.1 概述

01.Django定义
    a.框架本质
        Django是一个高级Python Web框架,遵循MTV(Model-Template-View)架构模式
        由Adrian Holovaty和Simon Willison于2003年在Lawrence Journal-World报社开发
        2005年以BSD许可证开源发布,现由Django软件基金会维护
        遵循"batteries included"理念,提供构建Web应用所需的大部分功能
    b.核心特性
        完整的ORM系统,支持多种数据库后端
        自动管理后台,快速构建CRUD界面
        强大的模板系统,支持模板继承和复用
        完善的用户认证和权限管理系统
        内置安全防护机制,防止常见Web攻击
        国际化支持,支持多语言开发
        丰富的中间件系统,可扩展性强
    c.适用场景
        适合构建复杂的企业级Web应用系统
        内容管理系统(CMS)和电商平台
        API后端服务,配合前端框架使用
        需要快速原型的项目开发
        团队协作开发,需要标准化框架

02.技术架构
    a.MTV架构模式
        Model(模型):数据访问层,处理与数据库相关的操作
        Template(模板):表现层,处理用户界面显示逻辑
        View(视图):业务逻辑层,处理用户请求和业务逻辑
        URLconf(URL配置):URL分发机制,将请求映射到视图函数
    b.WSGI兼容性
        实现WSGI(Web Server Gateway Interface)标准
        支持多种Web服务器:Gunicorn、uWSGI、mod_wsgi
        开发服务器:内置轻量级开发服务器
        生产部署:支持高性能的ASGI和WSGI服务器
    c.组件化设计
        松耦合的应用架构,支持模块化开发
        可重用的应用组件,便于团队协作
        插件式架构,支持第三方扩展
        清晰的层次分离,便于维护和测试

1.2 核心概念

01.项目与应用
    a.项目结构
        myproject/             # 项目根目录
        ├── manage.py         # 项目管理脚本
        ├── myproject/        # 项目配置包
        │   ├── __init__.py
        │   ├── settings.py   # 项目配置文件
        │   ├── urls.py       # 项目URL配置
        │   ├── wsgi.py       # WSGI配置
        │   └── asgi.py       # ASGI配置
        └── apps/             # 应用目录
    b.应用概念
        应用是项目的功能模块,负责特定业务逻辑
        一个项目可以包含多个应用
        应用可以重用到不同项目中
        每个应用有独立的models.py、views.py、urls.py等
    c.项目与应用关系
        项目是应用的容器,提供全局配置
        应用是具体的功能实现模块
        通过settings.py的INSTALLED_APPS注册应用
        应用间可以相互依赖和协作

02.模型系统
    a.ORM概念
        对象关系映射(Object-Relational Mapping)
        将Python对象映射到数据库表
        提供面向对象的数据库操作接口
        隐藏底层数据库差异,支持多种数据库
    b.模型定义
        每个模型类继承自django.db.models.Model
        类属性对应数据库表的字段
        支持各种数据类型:CharField、IntegerField、DateTimeField等
        字段选项:null、blank、default、unique等
    c.数据库迁移
        makemigrations:检测模型变更,生成迁移文件
        migrate:应用迁移到数据库,更新表结构
        支持数据库版本控制和回滚
        迁移文件记录了数据库结构的变更历史

03.视图系统
    a.函数视图
        a.功能说明
            最简单的视图形式,接收HttpRequest对象作为参数,执行业务逻辑后返回HttpResponse对象作为响应,适合处理简单的请求逻辑。
        b.代码示例
            ---
            from django.http import HttpResponse

            def hello_view(request):
                return HttpResponse("Hello, Django!")
            ---
    b.类视图
        a.功能说明
            基于类的视图提供更好的代码组织结构,支持HTTP方法分发(get、post、put、delete),支持Mixin模式组合多种功能,适合复杂业务逻辑。
        b.代码示例
            ---
            from django.views import View

            class HelloView(View):
                def get(self, request):
                    return HttpResponse("Hello, Class View!")
            ---
    c.通用视图
        Django提供的预定义视图类
        ListView、DetailView、CreateView、UpdateView、DeleteView
        快速实现常见的CRUD操作
        可通过继承和Mixin进行定制

04.模板系统
    a.模板语言
        变量显示:{{ variable }}
        标签:{% tag %}执行逻辑操作
        过滤器:{{ value|filter }}
        模板继承:{% extends "base.html" %}
        块定义:{% block content %}{% endblock %}
    b.模板继承
        基础模板定义页面结构
        子模板继承基础模板并重写特定块
        支持多级继承和块嵌套
        使用{{ block.super }}调用父块内容
    c.静态文件处理
        STATIC_URL:静态文件URL前缀
        STATICFILES_DIRS:静态文件存储目录
        {% load static %}加载静态文件标签
        {% static 'css/style.css' %}生成静态文件URL

1.3 发展历程

01.Django诞生背景
    a.起源故事
        2003年诞生于美国堪萨斯州的Lawrence Journal-World报社
        开发者:Adrian Holovaty和Simon Willison
        初始目标:快速开发新闻网站,提高新闻发布效率
        解决传统Web开发的重复性和低效性问题
    b.早期发展
        2005年7月以BSD许可证开源发布
        获得了Python社区的广泛关注和支持
        2008年成立Django软件基金会,独立运作
        版本迭代快速,功能不断完善
    c.社区成长
        全球活跃的开发者社区
        丰富的第三方应用和插件生态
        完善的文档和教程体系
        定期举办Django Under the Hood大会

02.版本演进历程
    a.Django 1.0时代(2008)
        2008年9月发布1.0正式版本
        确立了核心架构和主要功能
        引入Admin后台,成为框架标志性功能
        建立稳定的API和向后兼容性承诺
    b.Django 1.1-1.8系列(2009-2015)
        持续添加新功能:模型验证、聚合、多数据库支持
        改进Admin界面和用户体验
        增强安全性和性能
        建立成熟的第三方包生态
    c.Django 2.0时代(2017-2019)
        移除Python 2支持,专注Python 3发展
        引入路径转换器,简化URL配置
        改进移动端支持和响应式设计
        性能优化和开发者体验提升

03.现代Django发展
    a.Django 3.0系列(2019-2021)
        支持异步视图和中间件(async/await)
        引入MariaDB官方支持
        改进数据库查询性能和安全性
        优化开发服务器,支持HTTP/2
    b.Django 4.0系列(2021-2023)
        要求Python 3.8+,移除旧版本支持
        引入基于Redis的缓存后端
        改进表单渲染和安全性
        增强PostgreSQL支持和数据库功能
    c.Django 5.0系列(2023-现在)
        继续强化异步支持和性能
        改进Admin界面和用户体验
        增强安全特性和错误处理
        优化开发者工具和调试体验

04.技术影响和地位
    a.行业地位
        Python生态系统中最成熟的Web框架
        企业级应用开发的首选框架之一
        大型互联网公司的技术栈重要组成部分
        教育和培训领域的标准框架
    b.技术创新
        引入了"batteries included"的设计理念
        推动了Python Web开发的发展
        Admin后台成为其他框架模仿的对象
        ORM系统影响了后续的数据库抽象层设计
    c.开源贡献
        成功的开源项目管理典范
        活跃的社区贡献和维护模式
        良好的文档和代码质量标准
        为Python生态系统的繁荣做出重要贡献

1.4 设计哲学

01.DRY原则
    a.Don't Repeat Yourself
        避免代码重复,提高代码复用性
        一次定义,多处使用,减少维护成本
        Django的Admin后台是DRY原则的最佳实践
        自动生成代码,减少手工编写重复代码
    b.模板系统复用
        模板继承机制避免HTML代码重复
        模板包含(include)复用页面片段
        自定义标签和过滤器复用模板逻辑
        模板上下文处理器复用全局数据
    c.ORM设计思想
        定义一次模型,自动生成数据库操作
        迁移系统自动处理数据库结构变更
        通用视图减少重复的CRUD代码
        Mixin模式复用视图功能

02.显式优于隐式
    a.代码可读性
        明确的函数和类命名,见名知意
        清晰的参数传递和返回值
        避免隐式转换和魔法方法
        详细的错误信息和调试提示
    b.配置显式化
        settings.py中所有配置都是显式的
        数据库配置、中间件、应用注册等
        环境变量使用django-environ包管理
        生产环境和开发环境配置分离
    c.URL配置
        明确的URL路由映射
        路径参数和查询参数分离
        URL命名和反向解析避免硬编码
        包含机制组织大型应用的URL结构

03.快速开发
    a.开发效率优先
        内置常用功能,减少第三方依赖
        Admin后台自动生成管理界面
        开发服务器自动重载和错误提示
        脚手架工具快速生成项目结构
    b.约定优于配置
        遵循命名约定减少配置工作
        模型和数据库表的自动映射
        模板文件位置和命名约定
        静态文件组织结构约定
    c.开发者友好
        详细的错误页面和调试信息
        完善的文档和教程体系
        丰富的示例代码和最佳实践
        活跃的社区支持和技术交流

04.安全性优先
    a.内置安全防护
        CSRF保护:防止跨站请求伪造攻击
        XSS防护:自动转义用户输入数据
        SQL注入防护:ORM提供参数化查询
        点击劫持保护:X-Frame-Options头设置
    b.安全默认设置
        生产环境安全配置建议
        密码哈希存储,使用PBKDF2算法
        会话安全配置,支持安全的cookie设置
        HTTPS重定向和安全头设置
    c.安全最佳实践
        定期发布安全更新和补丁
        安全漏洞报告和响应机制
        安全文档和最佳实践指南
        与安全社区合作,及时发现和修复问题

1.5 优势与特点

01.开发效率优势
    a.快速开发
        内置完整的功能组件,无需重复开发
        Admin后台自动生成CRUD管理界面
        开发服务器支持自动重载和调试
        脚手架工具快速创建项目和应用结构
        示例代码:
        ```python
        # 快速创建项目和应用
        django-admin startproject myproject
        python manage.py startapp myapp
        python manage.py createsuperuser  # 创建管理员用户
        ```
    b.代码复用
        丰富的内置应用:auth、admin、sessions等
        强大的第三方包生态系统
        应用模块化设计,便于代码重用
        模板系统和通用视图减少重复代码
    c.开发工具
        django-admin和manage.py管理命令
        开发服务器支持调试和性能分析
        数据库迁移工具简化数据库管理
        自动化测试框架和代码生成工具

02.功能完整性
    a.核心功能
        完整的ORM系统,支持多种数据库
        强大的模板系统和前端集成
        用户认证和权限管理系统
        表单处理和数据验证机制
        文件上传和静态文件管理
    b.高级功能
        国际化和本地化支持
        缓存系统和性能优化
        中间件系统和请求处理管道
        信号系统和事件处理
        邮件发送和后台任务处理
    c.扩展性
        插件式架构,支持自定义扩展
        丰富的第三方包和应用
        API设计支持前后端分离
        微服务架构集成能力

03.安全特性
    a.内置安全防护
        CSRF保护:防止跨站请求伪造
        XSS防护:自动HTML转义
        SQL注入防护:参数化查询
        点击劫持保护:安全头设置
    b.数据安全
        密码哈希存储,支持多种算法
        会话安全配置,防止会话劫持
        文件上传安全验证和处理
        数据库连接安全配置
    c.生产安全
        安全的默认配置建议
        环境变量管理敏感信息
        HTTPS强制和安全头设置
        定期安全更新和漏洞修复

04.社区和生态
    a.活跃社区
        全球数百万开发者使用
        定期的技术会议和交流活动
        丰富的在线教程和文档资源
        活跃的开源贡献和社区维护
    b.第三方生态
        Django REST Framework构建API
        Django CMS构建内容管理系统
        Celery后台任务处理
        Django Channels实时通信
    c.企业支持
        大型互联网公司的生产使用
        专业的技术支持和咨询服务
        完善的培训和教育体系
        长期维护和支持承诺

1.6 应用场景

01.企业级Web应用
    a.业务管理系统
        Django适合构建复杂的企业级应用系统
        提供完整的用户认证、权限管理、数据校验功能
        支持多种数据库,满足企业数据存储需求
        内置Admin后台,快速构建管理系统界面
        示例应用:ERP系统、CRM系统、OA系统
    b.数据密集型应用
        强大的ORM适合处理复杂的数据关系
        支持大数据量的查询和操作优化
        数据库迁移系统简化数据结构变更
        完整的事务支持保证数据一致性
    c.多租户系统
        支持多用户和多组织的数据隔离
        灵活的权限系统和角色管理
        可扩展的架构支持业务增长
        安全的配置和部署方案

02.内容管理系统
    a.CMS开发
        Django最初就是为CMS开发而设计的
        强大的模板系统便于页面布局和内容展示
        Admin后台提供便捷的内容管理界面
        支持多媒体文件管理和版本控制
        示例:新闻网站、博客系统、企业官网
    b.媒体平台
        支持复杂的媒体内容关系和分类
        灵活的模板系统适应不同页面布局
        文件上传和处理能力
        多语言支持和国际化功能
    c.电商平台
        支持复杂的商品目录和订单管理系统
        内置用户认证和支付接口集成能力
        良好的安全性保障交易数据安全
        可扩展的架构支持业务快速增长

03.API后端服务
    a.Django REST Framework
        提供强大的API开发能力
        支持多种序列化格式(JSON、XML等)
        完整的认证和权限控制系统
        自动生成API文档,便于前后端协作
        -----------------------------------------------------------------------------------------------------
        from rest_framework import viewsets, serializers
        from django.contrib.auth.models import User

        class UserSerializer(serializers.ModelSerializer):
            class Meta:
                model = User
                fields = ['id', 'username', 'email']

        class UserViewSet(viewsets.ModelViewSet):
            queryset = User.objects.all()
            serializer_class = UserSerializer
    b.微服务架构
        Django可作为微服务中的API网关
        支持异步处理和实时通信
        与消息队列集成,支持分布式系统
        容器化部署和云原生支持
    c.移动应用后端
        提供RESTful API支持移动应用
        用户认证和推送通知功能
        数据同步和离线支持
        性能优化和缓存策略

04.教育和科研
    a.在线教育平台
        适合构建在线学习系统
        支持课程管理、用户权限、内容展示
        可扩展的架构支持教育业务需求
        良好的国际化支持多语言教学
        示例:Moodle、edX等平台的部分功能
    b.科研数据管理
        强大的数据建模和处理能力
        支持复杂的数据关系和查询
        数据可视化和报表功能
        科学计算和分析工具集成
    c.学术网站
        论文管理和发布系统
        学术会议管理平台
        研究项目管理系统
        学术资源分享平台

1.7 版本演进

01.Django 1.x时代(2005-2017)
    a.早期版本(1.0-1.4)
        2008年发布1.0版本,确立核心架构
        引入Admin后台,成为框架标志性功能
        建立ORM系统,提供数据库抽象层
        引入模板系统,分离逻辑和显示
    b.稳定发展期(1.5-1.8)
        持续添加新功能:模型验证、聚合查询
        改进Admin界面和用户体验
        增强安全性和性能优化
        建立成熟的第三方包生态
        -----------------------------------------------------------------------------------------------------
        # Django 1.8 语法示例
        from django.db import models
        from django.contrib.auth.models import User

        class Article(models.Model):
            title = models.CharField(max_length=200)
            content = models.TextField()
            author = models.ForeignKey(User, on_delete=models.CASCADE)
            created_date = models.DateTimeField(auto_now_add=True)
    c.长期支持版本
        Django 1.8 LTS:提供3年长期支持
        专注于稳定性和安全性修复
        企业级应用推荐使用LTS版本
        平滑升级路径和向后兼容

02.Django 2.x时代(2017-2019)
    a.重大变革(2.0-2.1)
        移除Python 2支持,专注Python 3发展
        引入路径转换器,简化URL配置
        改进Admin界面,提升用户体验
        增强移动端支持,响应式设计
    b.功能增强(2.2-2.2)
        Window表达式支持复杂SQL查询
        数据库函数和聚合功能增强
        改进的错误处理和调试信息
        性能优化和开发者体验提升
    c.安全更新
        定期发布安全补丁和更新
        修复已知安全漏洞和问题
        改进安全配置和默认设置
        加强安全文档和最佳实践

03.Django 3.x时代(2019-2021)
    a.异步支持(3.0-3.1)
        支持异步视图和中间件
        引入ASGI接口,支持WebSocket
        异步数据库查询和文件操作
        提升并发处理能力和性能
        -----------------------------------------------------------------------------------------------------
        import asyncio
        from django.http import JsonResponse
        from django.views import View

        class AsyncView(View):
            async def get(self, request):
                await asyncio.sleep(1)  # 模拟异步操作
                return JsonResponse({'status': 'ok'})
    b.数据库增强
        引入MariaDB官方支持
        改进数据库查询性能
        支持更多的数据库特性
        优化迁移系统和数据操作
    c.开发体验
        改进错误信息和调试功能
        优化开发服务器和性能
        增强测试框架和工具
        改进文档和示例代码

04.Django 4.x时代(2021-2023)
    a.版本升级要求
        移除对Python 3.7的支持,要求Python 3.8+
        弃用旧功能和配置选项
        清理过时的代码和API
        为未来版本奠定基础
    b.新功能特性
        引入基于Redis的缓存后端
        改进表单渲染和安全性
        增强PostgreSQL支持和特性
        优化开发服务器,支持HTTP/2
    c.性能和安全
        持续的性能优化和改进
        增强安全特性和防护机制
        改进错误处理和日志记录
        优化缓存和数据库操作

05.Django 5.x时代(2023-现在)
    a.当前发展(5.0-5.1)
        继续强化异步支持和性能
        改进Admin界面和用户体验
        增强安全特性和错误处理
        优化开发者工具和调试体验
    b.未来规划
        更好的云原生支持
        增强的API开发能力
        改进的性能和扩展性
        更完善的开发工具和生态
    c.社区发展
        活跃的开源社区贡献
        丰富的第三方包和插件
        完善的文档和教程体系
        全球范围的技术交流和合作

2 基础架构

2.1 MTV架构模式

01.MTV架构详解
    a.架构组成
        Model(模型):数据访问层,定义数据结构和业务逻辑
        Template(模板):表现层,处理用户界面显示
        View(视图):业务逻辑层,处理请求和响应
        Controller(控制器):URL路由系统,分发请求到视图
    b.与传统MVC对比
        MTV是MVC模式的变种,更适合Web开发
        Django的View对应MVC的Controller
        Django的Template对应MVC的View
        Django的Model与MVC的Model基本一致
        更好地体现了关注点分离原则
    c.架构优势
        清晰的层次分离,便于维护和测试
        高度模块化,支持团队协作开发
        松耦合设计,组件可独立替换和扩展
        符合软件工程的最佳实践原则

02.Model层详解
    a.数据模型定义
        a.功能说明
            每个模型类继承自django.db.models.Model,类属性对应数据库表的字段,
            支持各种数据类型(CharField、TextField、ForeignKey等)和关系类型,
            提供丰富的字段选项(max_length、null、default等)和验证机制。
        b.代码示例
            ---
            from django.db import models

            class BlogPost(models.Model):
                title = models.CharField(max_length=200)
                content = models.TextField()
                created_at = models.DateTimeField(auto_now_add=True)

                class Meta:
                    ordering = ['-created_at']
            ---
    b.ORM功能特性
        对象关系映射,Python对象到数据库表的映射
        强大的查询API,支持复杂的数据查询
        数据库迁移系统,自动处理结构变更
        事务支持,保证数据一致性
        多数据库支持,可在不同数据库间切换
    c.数据验证和约束
        字段级别的数据验证
        模型级别的完整性约束
        自定义验证器和清理方法
        表单集和模型表单集成

03.Template层详解
    a.模板语言
        Django模板语言(DTL)设计简单安全
        变量显示:{{ variable }}
        模板标签:{% tag %}执行逻辑
        过滤器:{{ value|filter }}
        注释:{# comment #}模板注释
    b.模板继承机制
        a.功能说明
            基础模板定义页面结构和公共部分,子模板通过{% extends %}继承基础模板并重写特定块,
            支持多级继承和块嵌套,使用{{ block.super }}可调用父块内容,实现模板复用。
        b.代码示例
            ---
            <!-- base.html -->
            <html>
            <head>
                <title>{% block title %}My Site{% endblock %}</title>
            </head>
            <body>
                {% block content %}{% endblock %}
            </body>
            </html>

            <!-- blog_post.html -->
            {% extends "base.html" %}
            {% block title %}{{ post.title }}{% endblock %}
            {% block content %}<h1>{{ post.title }}</h1>{% endblock %}
            ---
    c.模板上下文处理
        上下文处理器添加全局变量
        视图函数传递局部上下文
        模板标签和过滤器扩展功能
        安全的模板渲染机制

04.View层详解
    a.视图函数
        接收HttpRequest对象作为参数
        执行业务逻辑和数据处理
        返回HttpResponse对象作为响应
        支持多种响应类型:HTML、JSON、文件等
    b.类视图
        a.功能说明
            基于类的视图提供更好的代码组织结构,支持HTTP方法分发处理(get、post、put、delete),
            Mixin模式组合多种功能,通用视图快速实现CRUD操作,适合复杂业务场景。
        b.代码示例
            ---
            from django.views import View
            from django.shortcuts import render

            class BlogPostDetailView(View):
                def get(self, request, pk):
                    post = get_object_or_404(BlogPost, pk=pk)
                    return render(request, 'blog/detail.html', {'post': post})

                def post(self, request):
                    # 处理POST请求
                    return HttpResponse("Created")
            ---
    c.通用视图
        ListView:列表展示视图
        DetailView:详情展示视图
        CreateView:创建对象视图
        UpdateView:更新对象视图
        DeleteView:删除对象视图

2.2 项目结构详解

01.项目目录结构
    a.标准项目结构
        myproject/                 # 项目根目录
        ├── manage.py             # 项目管理脚本
        ├── myproject/            # 项目配置包
        │   ├── __init__.py       # Python包初始化文件
        │   ├── settings.py       # 项目配置文件
        │   ├── urls.py           # 项目URL配置
        │   ├── wsgi.py           # WSGI应用配置
        │   └── asgi.py           # ASGI应用配置
        └── apps/                 # 应用目录
    b.应用目录结构
        apps/
        ├── __init__.py           # 应用包初始化
        ├── models.py             # 数据模型定义
        ├── views.py              # 视图函数/类
        ├── urls.py               # 应用URL配置
        ├── forms.py              # 表单定义
        ├── admin.py              # Admin后台配置
        ├── apps.py               # 应用配置
        ├── tests.py              # 测试用例
        ├── migrations/           # 数据库迁移文件
        │   ├── 0001_initial.py
        │   └── ...
        ├── templates/            # 模板文件
        │   └── app_name/
        │       ├── base.html
        │       └── index.html
        └── static/               # 静态文件
            └── app_name/
                ├── css/
                ├── js/
                └── images/
    c.settings.py核心配置
        ---
        import os
        from pathlib import Path

        # 项目根目录
        BASE_DIR = Path(__file__).resolve().parent.parent

        # 调试模式
        DEBUG = True

        # 允许的主机
        ALLOWED_HOSTS = ['localhost', '127.0.0.1']

        # 应用注册
        INSTALLED_APPS = [
            'django.contrib.admin',
            'django.contrib.auth',
            'django.contrib.contenttypes',
            'django.contrib.sessions',
            'django.contrib.messages',
            'django.contrib.staticfiles',
            'apps.blog',           # 自定义应用
            'apps.users',          # 自定义应用
        ]

        # 中间件配置
        MIDDLEWARE = [
            'django.middleware.security.SecurityMiddleware',
            'django.contrib.sessions.middleware.SessionMiddleware',
            'django.middleware.common.CommonMiddleware',
            'django.middleware.csrf.CsrfViewMiddleware',
            'django.contrib.auth.middleware.AuthenticationMiddleware',
            'django.contrib.messages.middleware.MessageMiddleware',
            'django.middleware.clickjacking.XFrameOptionsMiddleware',
        ]

        # 数据库配置
        DATABASES = {
            'default': {
                'ENGINE': 'django.db.backends.postgresql',
                'NAME': 'myproject',
                'USER': 'username',
                'PASSWORD': 'password',
                'HOST': 'localhost',
                'PORT': '5432',
            }
        }

        # 国际化配置
        LANGUAGE_CODE = 'zh-hans'
        TIME_ZONE = 'Asia/Shanghai'
        USE_I18N = True
        USE_TZ = True

        # 静态文件配置
        STATIC_URL = '/static/'
        STATIC_ROOT = BASE_DIR / 'staticfiles'
        STATICFILES_DIRS = [
            BASE_DIR / 'static',
        ]

        # 媒体文件配置
        MEDIA_URL = '/media/'
        MEDIA_ROOT = BASE_DIR / 'media'

        # 安全设置
        SECRET_KEY = 'django-insecure-secret-key'
        ---

02.manage.py管理脚本
    a.常用命令
        startproject:创建新项目
        startapp:创建新应用
        runserver:启动开发服务器
        shell:启动Django shell
        makemigrations:创建数据库迁移
        migrate:应用数据库迁移
        createsuperuser:创建超级用户
        collectstatic:收集静态文件
        test:运行测试用例
    b.命令示例
        ---
        # 创建项目
        django-admin startproject myproject

        # 创建应用
        python manage.py startapp blog

        # 启动开发服务器
        python manage.py runserver 0.0.0.0:8000

        # 数据库操作
        python manage.py makemigrations
        python manage.py migrate

        # 创建超级用户
        python manage.py createsuperuser

        # 收集静态文件
        python manage.py collectstatic

        # 运行测试
        python manage.py test
        ---
    c.自定义管理命令
        a.功能说明
            在应用目录下创建management/commands/目录,继承BaseCommand类创建自定义命令,可添加参数解析和业务逻辑处理,实现项目特定的管理任务。
        b.代码示例
            ---
            # blog/management/commands/cleanup_posts.py
            from django.core.management.base import BaseCommand
            from apps.blog.models import BlogPost

            class Command(BaseCommand):
                help = '清理旧博客文章'

                def add_arguments(self, parser):
                    parser.add_argument('--days', type=int, default=30)

                def handle(self, *args, **options):
                    deleted_count = BlogPost.objects.filter(
                        is_published=False
                    ).delete()[0]
                    self.stdout.write(f'成功删除 {deleted_count} 篇')
            ---

03.应用配置
    a.apps.py配置类
        a.功能说明
            定义应用名称和标签,配置应用行为和选项,实现应用级别的初始化逻辑,可在ready方法中注册信号等启动任务。
        b.代码示例
            ---
            from django.apps import AppConfig

            class BlogConfig(AppConfig):
                name = 'apps.blog'
                verbose_name = '博客应用'

                def ready(self):
                    import apps.blog.signals
            ---
    b.__init__.py配置
        定义应用包的初始化逻辑
        设置默认应用配置类
        导入必要的模块和函数
    c.应用间依赖
        定义应用间的依赖关系
        处理循环依赖问题
        实现应用间的通信和协作
        使用信号机制解耦应用

2.3 请求处理流程

01.完整请求周期
    a.请求到达
        用户通过浏览器发送HTTP请求
        Web服务器(如Nginx、Apache)接收请求
        通过WSGI接口将请求传递给Django应用
        Django开始处理HTTP请求
    b.URL路由匹配
        a.功能说明
            Django的URLconf根据URL路径匹配对应的视图函数,按照配置的顺序进行URL模式匹配,支持正则表达式和路径转换器,提取URL参数并传递给视图函数。
        b.代码示例
            ---
            # urls.py
            from django.urls import path
            from . import views

            urlpatterns = [
                path('blog/', views.blog_list),
                path('blog/<int:post_id>/', views.blog_detail),
            ]

            # views.py
            def blog_detail(request, post_id):
                post = get_object_or_404(BlogPost, pk=post_id)
                return render(request, 'blog/detail.html', {'post': post})
            ---
    c.中间件处理
        中间件按照MIDDLEWARE配置的顺序执行
        每个中间件的process_request方法依次执行
        如果中间件返回响应,后续处理停止
        视图函数执行前的预处理

02.视图函数执行
    a.视图函数调用
        Django调用匹配到的视图函数
        传递HttpRequest对象和URL参数
        视图函数执行业务逻辑处理
        可以访问请求的所有信息:GET、POST、FILES、user等
    b.业务逻辑处理
        a.功能说明
            在视图函数中执行数据库操作(查询、创建、更新、删除),进行业务规则验证和处理,实现数据转换和计算,调用其他服务和API。
        b.代码示例
            ---
            from django.shortcuts import render, redirect
            from django.contrib.auth.decorators import login_required
            from .forms import BlogPostForm

            @login_required
            def create_post(request):
                if request.method == 'POST':
                    form = BlogPostForm(request.POST)
                    if form.is_valid():
                        post = form.save(commit=False)
                        post.author = request.user
                        post.save()
                        return redirect('blog:detail', pk=post.pk)
                else:
                    form = BlogPostForm()
                return render(request, 'blog/create.html', {'form': form})
            ---
    c.数据库操作
        使用ORM进行数据库查询和操作
        支持事务处理保证数据一致性
        数据验证和错误处理
        性能优化和缓存策略

03.响应生成
    a.HttpResponse对象
        a.功能说明
            视图函数返回HttpResponse对象,包含响应内容、状态码、响应头,支持多种内容类型(HTML、JSON、XML、文件等)。
        b.代码示例
            ---
            from django.http import JsonResponse, HttpResponseRedirect

            def json_response(request):
                data = {'status': 'success', 'data': {'id': 1}}
                return JsonResponse(data)

            def redirect_response(request):
                return HttpResponseRedirect('/blog/')
            ---
    b.模板渲染
        视图函数使用模板引擎生成HTML内容
        传递上下文数据到模板
        模板系统处理变量替换和逻辑执行
        生成最终的HTML响应内容
    c.中间件后处理
        中间件按照逆序执行process_response方法
        可以修改响应内容和响应头
        处理响应的后处理逻辑
        设置缓存头、安全头等

04.响应返回
    a.WSGI接口
        Django通过WSGI接口返回HTTP响应
        Web服务器接收响应并返回给用户
        支持多种WSGI服务器:Gunicorn、uWSGI等
        异步支持:ASGI接口处理异步请求
    b.日志记录
        记录请求和响应信息
        错误和异常日志记录
        性能监控和统计分析
        安全事件记录和报警
    c.性能优化
        数据库查询优化
        缓存策略实施
        静态文件优化
        代码性能分析和改进

2.4 WSGI接口

01.WSGI标准详解
    a.WSGI定义
        Web Server Gateway Interface的缩写
        Python Web应用与Web服务器之间的标准接口
        定义了服务器如何与应用程序通信的规范
        实现了Web服务器与应用程序的解耦
    b.WSGI协议规范
        应用程序必须是可调用对象
        接受两个参数:environ字典和start_response函数
        返回可迭代的响应体
        environ包含请求环境和CGI变量
        start_response设置响应状态和头部
    c.Django WSGI实现
        a.功能说明
            django.core.wsgi模块提供WSGI适配器,自动处理请求解析和响应生成,支持同步和异步处理模式,兼容标准的WSGI服务器。
        b.代码示例
            ---
            # myproject/wsgi.py
            import os
            from django.core.wsgi import get_wsgi_application

            os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
            application = get_wsgi_application()
            ---

02.开发服务器
    a.runserver命令
        Django内置的开发服务器
        支持自动重载和调试功能
        适合开发和测试环境
        不适合生产环境使用
    b.开发服务器特性
        自动检测文件变化并重启
        详细的错误页面和调试信息
        支持HTTPS开发(runserver_plus)
        集成静态文件服务
    c.开发服务器配置
        命令行选项:
        ---
        python manage.py runserver 0.0.0.0:8000
        python manage.py runserver --settings=myproject.settings_dev
        python manage.py runserver --noreload
        python manage.py runserver --verbosity=2
        ---
    d.开发限制
        性能限制:不适合高并发
        安全性限制:不适合生产环境
        稳定性限制:可能存在bug
        功能限制:缺少生产级特性

03.生产WSGI服务器
    a.Gunicorn服务器
        a.功能说明
            成熟的Python WSGI服务器,支持多进程和多线程,轻量级且性能优秀,广泛应用于生产环境。
        b.代码示例
            ---
            # 安装和启动
            pip install gunicorn
            gunicorn myproject.wsgi:application --bind 0.0.0.0:8000

            # gunicorn.conf.py
            bind = "0.0.0.0:8000"
            workers = 4
            timeout = 30
            loglevel = "info"
            ---
    b.uWSGI服务器
        a.功能说明
            功能强大的应用服务器,支持WSGI和ASGI协议,丰富的配置选项和插件,高性能和稳定性。
        b.代码示例
            ---
            # uwsgi.ini
            [uwsgi]
            socket = 127.0.0.1:8000
            chdir = /path/to/myproject
            wsgi-file = myproject/wsgi.py
            master = true
            processes = 4
            threads = 2
            ---
    c.mod_wsgi集成
        a.功能说明
            Apache服务器的WSGI模块,直接集成到Apache服务器,支持嵌入式和守护进程模式,适合已有Apache基础设施。
        b.代码示例
            ---
            # /etc/apache2/sites-available/myproject.conf
            <VirtualHost *:80>
                ServerName myproject.com
                Alias /static /path/to/myproject/static
                WSGIDaemonProcess myproject python-path=/path/to/myproject
                WSGIProcessGroup myproject
                WSGIScriptAlias / /path/to/myproject/wsgi.py
            </VirtualHost>
            ---

04.ASGI异步支持
    a.ASGI接口
        异步服务器网关接口
        支持WebSocket和HTTP/2
        异步处理提升并发性能
        Django 3.0+原生支持ASGI
    b.ASGI服务器
        a.功能说明
            Daphne是Django官方ASGI服务器,Uvicorn是基于Starlette的高性能ASGI服务器,Hypercorn支持HTTP/2和WebSocket。
        b.代码示例
            ---
            # 安装和启动
            pip install daphne uvicorn
            daphne myproject.asgi:application -b 0.0.0.0 -p 8000
            uvicorn myproject.asgi:application --host 0.0.0.0 --port 8000 --workers 4
            ---
    c.异步视图
        a.功能说明
            Django 3.1+支持异步视图函数,使用async/await语法,支持异步数据库查询和文件操作。
        b.代码示例
            ---
            from django.http import JsonResponse
            from asgiref.sync import sync_to_async

            async def async_api_view(request):
                data = await sync_to_async(get_data)()
                return JsonResponse({'data': data})

            def get_data():
                return {'id': 1, 'name': '异步数据'}
            ---

05.部署最佳实践
    a.服务器选择
        Gunicorn:适合传统WSGI应用
        uWSGI:功能丰富,性能优秀
        Daphne:Django官方ASGI服务器
        Uvicorn:高性能ASGI服务器
    b.性能优化
        合理配置worker进程数
        使用进程和线程混合模式
        启用预加载应用
        配置合理的超时和连接限制
    c.监控和日志
        配置详细的访问日志和错误日志
        使用系统监控工具监控服务器状态
        设置报警和自动重启机制
        收集性能指标和统计数据

2.5 配置系统

01.配置文件结构
    a.settings.py核心配置
        a.功能说明
            项目的主要配置文件
            包含数据库、中间件、应用注册等设置
            支持模块化配置和条件配置
            分离开发和生产环境配置
        b.配置文件组织
            ---
            # settings.py
            import os
            from pathlib import Path
            import json

            # 基础配置
            BASE_DIR = Path(__file__).resolve().parent.parent
            SECRET_KEY = os.environ.get('SECRET_KEY', 'django-insecure-key')

            # 环境检测
            ENVIRONMENT = os.environ.get('DJANGO_ENV', 'development')

            # 导入环境特定配置
            if ENVIRONMENT == 'production':
                from .production import *
            elif ENVIRONMENT == 'testing':
                from .testing import *
            else:
                from .development import *

            # 自定义配置加载
            try:
                with open(BASE_DIR / 'config' / 'custom.json') as f:
                    custom_config = json.load(f)
                    locals().update(custom_config)
            except FileNotFoundError:
                pass
            ---
    b.环境特定配置
        开发环境:development.py
        测试环境:testing.py
        生产环境:production.py
        本地环境:local.py(不纳入版本控制)
    c.配置模块化
        按功能模块组织配置
        数据库配置、缓存配置、日志配置等
        使用配置类管理复杂配置
        支持配置继承和覆盖

02.核心配置项
    a.基础配置
        DEBUG:调试模式开关
        ALLOWED_HOSTS:允许访问的主机列表
        SECRET_KEY:安全密钥
        ROOT_URLCONF:根URL配置模块
        WSGI_APPLICATION:WSGI应用
    b.应用配置
        a.功能说明
            INSTALLED_APPS注册应用列表,包含Django内置应用和自定义应用,支持应用名称和配置类。
        b.代码示例
            ---
            INSTALLED_APPS = [
                'django.contrib.admin',
                'django.contrib.auth',
                'rest_framework',
                'apps.users',
                'apps.blog',
            ]
            ---
    c.中间件配置
        a.功能说明
            MIDDLEWARE中间件列表按执行顺序排列,支持字符串和类的引用方式。
        b.代码示例
            ---
            MIDDLEWARE = [
                'django.middleware.security.SecurityMiddleware',
                'django.contrib.sessions.middleware.SessionMiddleware',
                'django.middleware.csrf.CsrfViewMiddleware',
                'apps.core.middleware.RequestLogMiddleware',
            ]
            ---
    d.数据库配置
        a.功能说明
            DATABASES数据库连接配置,支持多种数据库后端,支持读写分离和分片。
        b.代码示例
            ---
            DATABASES = {
                'default': {
                    'ENGINE': 'django.db.backends.postgresql',
                    'NAME': 'myproject',
                    'USER': 'postgres',
                    'HOST': 'localhost',
                    'PORT': '5432',
                }
            }
            ---

03.安全配置
    a.安全中间件
        SecurityMiddleware:安全相关中间件
        SECURE_BROWSER_XSS_FILTER
        SECURE_CONTENT_TYPE_NOSNIFF
        SECURE_HSTS_SECONDS
        SECURE_HSTS_INCLUDE_SUBDOMAINS
        SECURE_HSTS_PRELOAD
    b.认证安全
        a.功能说明
            配置密码哈希器、会话安全设置和CSRF保护,确保应用安全性。
        b.代码示例
            ---
            PASSWORD_HASHERS = [
                'django.contrib.auth.hashers.Argon2PasswordHasher',
                'django.contrib.auth.hashers.PBKDF2PasswordHasher',
            ]

            SESSION_COOKIE_SECURE = True
            SESSION_COOKIE_HTTPONLY = True
            CSRF_COOKIE_SECURE = True
            ---
    c.权限配置
        AUTH_USER_MODEL:自定义用户模型
        AUTH_PASSWORD_VALIDATORS:密码验证器
        LOGIN_URL:登录页面URL
        LOGIN_REDIRECT_URL:登录后重定向URL

04.静态文件和媒体文件
    a.静态文件配置
        STATIC_URL:静态文件URL前缀
        STATIC_ROOT:静态文件收集目录
        STATICFILES_DIRS:静态文件搜索目录
        STATICFILES_FINDERS:静态文件查找器
    b.媒体文件配置
        MEDIA_URL:媒体文件URL前缀
        MEDIA_ROOT:媒体文件存储目录
        文件上传处理配置
    c.文件存储后端
        a.功能说明
            Django-storages支持云存储,可自定义存储类,支持文件压缩和处理。
        b.代码示例
            ---
            STATIC_URL = '/static/'
            STATIC_ROOT = BASE_DIR / 'staticfiles'
            MEDIA_URL = '/media/'
            MEDIA_ROOT = BASE_DIR / 'media'
            FILE_UPLOAD_MAX_MEMORY_SIZE = 5242880  # 5MB

            # AWS S3存储
            DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
            AWS_STORAGE_BUCKET_NAME = 'mybucket'
            ---

05.缓存配置
    a.缓存后端
        Redis缓存:高性能内存缓存
        Memcached缓存:分布式缓存系统
        数据库缓存:使用数据库表缓存
        文件系统缓存:本地文件缓存
    b.缓存配置示例
        ---
        # Redis缓存配置
        CACHES = {
            'default': {
                'BACKEND': 'django_redis.cache.RedisCache',
                'LOCATION': 'redis://127.0.0.1:6379/1',
                'OPTIONS': {
                    'CLIENT_CLASS': 'django_redis.client.DefaultClient',
                    'CONNECTION_POOL_KWARGS': {
                        'max_connections': 50,
                        'retry_on_timeout': True,
                    }
                },
                'KEY_PREFIX': 'myproject',
                'TIMEOUT': 300,  # 5分钟
            }
        }

        # 多级缓存配置
        CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
            },
            'session': {
                'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
                'LOCATION': 'cache_table',
            },
            'static': {
                'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
                'LOCATION': '/tmp/django_cache',
            }
        }

        # 缓存配置
        CACHE_MIDDLEWARE_ALIAS = 'default'
        CACHE_MIDDLEWARE_SECONDS = 600
        CACHE_MIDDLEWARE_KEY_PREFIX = ''
        ---
    c.会话缓存
        SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
        SESSION_CACHE_ALIAS = 'session'
        SESSION_COOKIE_AGE = 3600  # 1小时

2.6 环境管理

01.虚拟环境
    a.venv虚拟环境
        a.功能说明
            Python 3.3+内置虚拟环境,轻量级无需额外安装,创建和使用简单。
        b.代码示例
            ---
            # 创建虚拟环境
            python -m venv myproject-env

            # 激活虚拟环境
            source myproject-env/bin/activate  # Linux/macOS
            myproject-env\Scripts\activate  # Windows

            # 安装依赖
            pip install -r requirements.txt
            ---
    b.virtualenv虚拟环境
        a.功能说明
            功能更丰富的虚拟环境工具,支持Python版本切换和不同的创建参数。
        b.代码示例
            ---
            pip install virtualenv
            virtualenv -p python3.9 myproject-env
            source myproject-env/bin/activate
            ---
    c.conda环境
        a.功能说明
            Anaconda的数据科学平台,集成包管理和环境管理,支持复杂的依赖关系。
        b.代码示例
            ---
            conda create -n myproject python=3.9
            conda activate myproject
            conda install django psycopg2
            conda env export > environment.yml
            ---

02.依赖管理
    a.requirements.txt
        a.功能说明
            标准的Python依赖管理文件,支持指定版本和安装选项,与pip工具完美集成。
        b.代码示例
            ---
            Django>=4.0,<5.0
            psycopg2-binary==2.9.3
            redis==4.3.1
            gunicorn==20.1.0
            ---
    b.Pipenv工具
        a.功能说明
            现代Python依赖管理工具,集成虚拟环境和依赖管理,支持Pipfile和Pipfile.lock。
        b.代码示例
            ---
            pip install pipenv
            pipenv install django==4.0.6
            pipenv install pytest --dev
            pipenv shell
            pipenv run python manage.py runserver
            ---
    c.Poetry工具
        a.功能说明
            现代化的Python项目管理工具,集成依赖管理、构建和发布,使用pyproject.toml配置文件。
        b.代码示例
            ---
            # pyproject.toml
            [tool.poetry]
            name = "myproject"
            version = "1.0.0"

            [tool.poetry.dependencies]
            python = "^3.8"
            django = "^4.0"
            ---

03.环境变量管理
    a.环境变量配置
        a.功能说明
            使用.env文件管理敏感信息,支持不同环境的配置,集成django-environ包。
        b.代码示例
            ---
            # settings.py
            import environ

            env = environ.Env(DEBUG=(bool, False))
            environ.Env.read_env(BASE_DIR / '.env')

            SECRET_KEY = env('SECRET_KEY')
            DEBUG = env('DEBUG')
            ---
    b..env文件示例
        ---
        # .env文件
        SECRET_KEY=your-secret-key-here
        DEBUG=True
        DATABASE_URL=postgresql://user:password@localhost:5432/myproject
        REDIS_URL=redis://localhost:6379/0
        EMAIL_URL=smtp://user:[email protected]:587
        ---
    c.环境配置分离
        开发环境:.env.development
        测试环境:.env.testing
        生产环境:.env.production
        本地环境:.env.local

04.配置管理最佳实践
    a.配置原则
        敏感信息不提交到版本控制
        使用环境变量管理配置
        分离不同环境的配置
        使用配置管理工具
    b.部署配置
        Docker容器化配置
        Kubernetes配置管理
        CI/CD管道配置
        云平台配置管理
    c.监控和日志
        配置监控工具
        设置日志记录
        配置报警机制
        性能监控配置

3 模型与ORM

3.1 模型定义与字段类型

01.模型类基础
    a.模型类定义
        a.功能说明
            Django模型类通过继承models.Model实现ORM映射,每个类属性对应数据库表字段,
            系统自动生成主键id,支持Meta类配置表名、排序等元数据,可重写save等方法实现自定义逻辑。
        b.代码示例
            ---
            from django.db import models

            class BlogPost(models.Model):
                title = models.CharField(max_length=200)
                author = models.ForeignKey(User, on_delete=models.CASCADE)
                created_at = models.DateTimeField(auto_now_add=True)

                class Meta:
                    ordering = ['-created_at']
            ---
    b.模型元数据
        a.功能说明
            Meta类用于配置模型元数据,包括自定义表名、默认排序规则、显示名称、联合唯一约束、索引优化和数据完整性约束,提供灵活的数据库层面控制能力。
        b.代码示例
            ---
            class Product(models.Model):
                name = models.CharField(max_length=100)
                price = models.DecimalField(max_digits=10, decimal_places=2)

                class Meta:
                    db_table = 'inventory_products'
                    ordering = ['name']
                    constraints = [
                        models.CheckConstraint(check=models.Q(price__gte=0), name='price_positive')
                    ]
            ---
    c.模型方法
        a.功能说明
            模型方法包括实例方法处理单个对象业务逻辑、类方法执行批量操作、属性方法提供动态计算值、魔法方法定制对象行为,实现业务逻辑封装和代码复用。
        b.代码示例
            ---
            class Order(models.Model):
                status = models.CharField(max_length=20, default='pending')

                def can_cancel(self):
                    return self.status in ['pending', 'processing']

                @property
                def status_display(self):
                    return dict(self.STATUS_CHOICES).get(self.status)

                @classmethod
                def get_pending_orders(cls):
                    return cls.objects.filter(status='pending')
            ---

02.基础字段类型
    a.字符串字段
        a.功能说明
            字符串字段提供多种类型满足不同需求,CharField存储定长文本、TextField存储长文本、EmailField和URLField自动验证格式、
            SlugField生成URL友好标识,支持配置长度、验证和显示选项。
        b.代码示例
            ---
            class Article(models.Model):
                title = models.CharField(max_length=200)
                slug = models.SlugField(unique=True)
                content = models.TextField()
                email = models.EmailField()
            ---
    b.数值字段
        a.功能说明
            数值字段支持整数、大整数、小整数、浮点数和精确小数类型,IntegerField存储整数、FloatField存储浮点数、
            DecimalField精确存储货币等小数值,可配置默认值和空值选项。
        b.代码示例
            ---
            class Product(models.Model):
                stock = models.IntegerField(default=0)
                weight = models.FloatField(null=True)
                price = models.DecimalField(max_digits=10, decimal_places=2)
            ---
    c.日期时间字段
        a.功能说明
            日期时间字段支持日期、时间、日期时间和时间间隔类型,DateField存储日期、TimeField存储时间、
            DateTimeField存储完整时间戳,支持auto_now和auto_now_add自动更新时间。
        b.代码示例
            ---
            class Event(models.Model):
                start_date = models.DateField()
                start_datetime = models.DateTimeField()
                created_at = models.DateTimeField(auto_now_add=True)
            ---

03.关系字段类型
    a.外键字段
        a.功能说明
            ForeignKey实现一对多关系,必须指定on_delete删除策略,支持related_name配置反向关系名称,
            可设置null和blank允许空值,提供CASCADE、SET_NULL、PROTECT等多种删除行为。
        b.代码示例
            ---
            class Author(models.Model):
                name = models.CharField(max_length=100)
                email = models.EmailField(unique=True)
                bio = models.TextField()
            class Book(models.Model):
                title = models.CharField(max_length=200)
                author = models.ForeignKey()
                co_author = models.ForeignKey()
                publisher = models.ForeignKey()
            ---
    b.多对多字段
        a.功能说明
            ManyToManyField实现多对多关系,支持through参数指定自定义中间表存储额外字段,
            symmetrical控制关系对称性,blank允许空关系,适用于标签、分类等多对多场景。
        b.代码示例
            ---
            class Tag(models.Model):
                name = models.CharField(max_length=50, unique=True)
                color = models.CharField(max_length=20, default='blue')
                def __str__(self):
                    return self.name
            class Category(models.Model):
                name = models.CharField(max_length=100)
                description = models.TextField()
                def __str__(self):
                    return self.name
            class Article(models.Model):
                title = models.CharField(max_length=200)
                content = models.TextField()
                tags = models.ManyToManyField()
                categories = models.ManyToManyField()
                related_articles = models.ManyToManyField()
            class Student(models.Model):
                name = models.CharField(max_length=100)
            class Course(models.Model):
                name = models.CharField(max_length=100)
                students = models.ManyToManyField()
            class Enrollment(models.Model):
                student = models.ForeignKey(on_delete=models.CASCADE)
                course = models.ForeignKey(on_delete=models.CASCADE)
                enrollment_date = models.DateField(auto_now_add=True)
                grade = models.CharField(max_length=2)
            class Meta:
                unique_together = ['student', 'course']
            ---
    c.一对一字段
        a.功能说明
            ForeignKey实现一对多关系,必须指定on_delete删除策略,支持related_name配置反向关系名称,
            可设置null和blank允许空值,提供CASCADE、SET_NULL、PROTECT等多种删除行为。
        b.代码示例
            ---
            class User(models.Model):
                username = models.CharField(max_length=50, unique=True)
                email = models.EmailField(unique=True)
            class UserProfile(models.Model):
                user = models.OneToOneField()
                avatar = models.ImageField()
                bio = models.TextField()
                birth_date = models.DateField()
                phone = models.CharField()
            from django.contrib.auth.models import User
            class ExtendedUser(models.Model):
                user = models.OneToOneField()
                company = models.CharField()
                department = models.CharField()
            ---

04.字段选项和约束
    a.通用字段选项
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            class Employee(models.Model):
                name = models.CharField()
                gender = models.CharField()
                email = models.EmailField()
                phone = models.CharField()
                salary = models.DecimalField()
                hire_date = models.DateField()
            ---
    b.数据库约束
        a.功能说明
            ForeignKey实现一对多关系,必须指定on_delete删除策略,支持related_name配置反向关系名称,
            可设置null和blank允许空值,提供CASCADE、SET_NULL、PROTECT等多种删除行为。
        b.代码示例
            ---
            class Product(models.Model):
                name = models.CharField(max_length=100)
                price = models.DecimalField(max_digits=10, decimal_places=2)
                quantity = models.IntegerField(default=0)
                category = models.CharField(max_length=50)
                is_active = models.BooleanField()
            class Meta:
                constraints = [
                    models.CheckConstraint(check=models.Q(price__gte=0), name='price_positive')
                ]
            ---
    c.自定义字段
        a.功能说明
            自定义字段通过继承models.Field或现有字段类型实现特殊需求,重写db_type指定数据库类型、from_db_value转换数据库值、
            get_prep_value准备保存值、validate实现自定义验证逻辑。
        b.代码示例
            ---
            from django.db import models
            from django.core.exceptions import ValidationError
            import re
            class ColorField(models.CharField):
                def __init__(self, *args, **kwargs):
                def from_db_value(self, value, expression, connection):
                        return value
                    return value
                def get_prep_value(self, value):
                        return value
                    return value
            class Product(models.Model):
                name = models.CharField(max_length=100)
            ---

3.2 模型关系

01.一对多关系
    a.外键定义
        a.功能说明
            ForeignKey实现一对多关系,必须指定on_delete删除策略,支持related_name配置反向关系名称,
            可设置null和blank允许空值,提供CASCADE、SET_NULL、PROTECT等多种删除行为。
        b.代码示例
            ---
            class Department(models.Model):
                name = models.CharField(max_length=100, unique=True)
                description = models.TextField()
                manager = models.ForeignKey()
                def __str__(self):
                    return self.name
            class Employee(models.Model):
                name = models.CharField(max_length=50)
                email = models.EmailField(unique=True)
                gender = models.CharField(max_length=1)
                birth_date = models.DateField(null=True)
                hire_date = models.DateField()
                salary = models.DecimalField(max_digits=10, decimal_places=2)
                department = models.ForeignKey()
                supervisor = models.ForeignKey()
            class Meta:
                ordering = ['name']
                def __str__(self):
                    return f'{self.name} - {self.department.name}'
                def get_subordinates_count(self):
                    return self.subordinates.count()
                        return False
                            return True
                    return False
            ---
    b.级联删除选项
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            class User(models.Model):
                name = models.CharField(max_length=50)
                email = models.EmailField(unique=True)
            class Profile(models.Model):
                user = models.OneToOneField()
                avatar = models.ImageField()
            class Blog(models.Model):
                author = models.ForeignKey()
                title = models.CharField(max_length=200)
                content = models.TextField()
            class Comment(models.Model):
                blog = models.ForeignKey()
                author = models.ForeignKey()
                content = models.TextField()
                created_at = models.DateTimeField(auto_now_add=True)
            class Category(models.Model):
                name = models.CharField(max_length=100)
                parent = models.ForeignKey()
            ---
    c.反向关系查询
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            from django.db.models import Count
            from django.db.models import Q
            ---

02.多对多关系
    a.基本多对多关系
        a.功能说明
            ManyToManyField实现多对多关系,支持through参数指定自定义中间表存储额外字段,
            symmetrical控制关系对称性,blank允许空关系,适用于标签、分类等多对多场景。
        b.代码示例
            ---
            class Student(models.Model):
                name = models.CharField(max_length=50)
                email = models.EmailField(unique=True)
                enrollment_date = models.DateField(auto_now_add=True)
                def __str__(self):
                    return self.name
            class Course(models.Model):
                name = models.CharField(max_length=100)
                description = models.TextField()
                credits = models.IntegerField(default=3)
                start_date = models.DateField()
                def __str__(self):
                    return self.name
            class Enrollment(models.Model):
                student = models.ForeignKey(on_delete=models.CASCADE)
                course = models.ForeignKey(on_delete=models.CASCADE)
                enrollment_date = models.DateField(auto_now_add=True)
                grade = models.CharField(max_length=2)
                is_completed = models.BooleanField()
            class Meta:
                unique_together = ['student', 'course']
            class Tag(models.Model):
                name = models.CharField(max_length=50, unique=True)
                def __str__(self):
                    return self.name
            class Article(models.Model):
                title = models.CharField(max_length=200)
                content = models.TextField()
                tags = models.ManyToManyField()
                authors = models.ManyToManyField()
                return self.title
            ---
    b.中间表多对多关系
        a.功能说明
            ManyToManyField实现多对多关系,支持through参数指定自定义中间表存储额外字段,
            symmetrical控制关系对称性,blank允许空关系,适用于标签、分类等多对多场景。
        b.代码示例
            ---
            class Student(models.Model):
                name = models.CharField(max_length=50)
                email = models.EmailField(unique=True)
            class Course(models.Model):
                name = models.CharField(max_length=100)
                description = models.TextField()
            class Enrollment(models.Model):
                student = models.ForeignKey(on_delete=models.CASCADE)
                course = models.ForeignKey(on_delete=models.CASCADE)
                enrollment_date = models.DateField(auto_now_add=True)
                status = models.CharField()
                grade = models.CharField()
                feedback = models.TextField()
            class Meta:
                unique_together = ['student', 'course']
                ordering = ['-enrollment_date']
                def __str__(self):
                    return f'{self.student.name} - {self.course.name}'
                def is_passing(self):
                    return self.grade in ['A', 'B', 'C']
            class Course(models.Model):
                name = models.CharField(max_length=100)
                description = models.TextField()
                students = models.ManyToManyField()
                def get_enrolled_students(self):
                    return self.students.filter(
                    return self.students.filter(
                    return self.students.filter(
            ---
    c.多对多关系操作
        a.功能说明
            ManyToManyField实现多对多关系,支持through参数指定自定义中间表存储额外字段,
            symmetrical控制关系对称性,blank允许空关系,适用于标签、分类等多对多场景。
        b.代码示例
            ---
            from django.db.models import Q
            from django.db.models import Count
            ---

03.一对一关系
    a.基本一对一关系
        a.功能说明
            ForeignKey实现一对多关系,必须指定on_delete删除策略,支持related_name配置反向关系名称,
            可设置null和blank允许空值,提供CASCADE、SET_NULL、PROTECT等多种删除行为。
        b.代码示例
            ---
            from django.contrib.auth.models import User
            class UserProfile(models.Model):
                user = models.OneToOneField()
                avatar = models.ImageField()
                bio = models.TextField()
                birth_date = models.DateField()
                phone = models.CharField()
                address = models.TextField()
                website = models.URLField()
                github_username = models.CharField()
                def __str__(self):
                    return f'{self.user.username}的个人资料'
                def get_age(self):
                    from datetime import date
                        return today.year - self.birth_date.year - (
                    return None
                def get_github_url(self):
                        return f'https://github.com/{self.github_username}'
                    return None
                return profile
                    return user.profile
                    return None
            ---
    b.模型扩展
        a.功能说明
            Django提供内置User模型用于用户管理,支持自定义用户模型配置扩展字段,认证后端负责验证用户身份和权限,可配置多个认证后端实现灵活的认证策略。
        b.代码示例
            ---
            from django.contrib.auth.models import AbstractUser
            from django.db import models
            class CustomUser(AbstractUser):
                phone = models.CharField()
                avatar = models.ImageField()
                birth_date = models.DateField()
                website = models.URLField()
                bio = models.TextField()
                is_verified = models.BooleanField()
                verification_token = models.CharField()
                def __str__(self):
                    return self.username
                def get_full_name(self):
                    return full_name or self.username
            class UserPreference(models.Model):
                user = models.OneToOneField()
                theme = models.CharField()
                language = models.CharField()
                timezone = models.CharField()
                email_notifications = models.BooleanField()
                push_notifications = models.BooleanField()
                def __str__(self):
                    return f'{self.user.username}的偏好设置'
            ---
    c.关系查询优化
        a.功能说明
            ForeignKey实现一对多关系,必须指定on_delete删除策略,支持related_name配置反向关系名称,
            可设置null和blank允许空值,提供CASCADE、SET_NULL、PROTECT等多种删除行为。
        b.代码示例
            ---
            def get_users_with_details():
                return CustomUser.objects.select_related(
            def user_list_view(request):
                return JsonResponse({'users': user_data})
            ---

3.3 数据库迁移系统

01.迁移基础概念
    a.迁移文件作用
        记录数据库结构的变更历史
        实现数据库版本控制
        支持团队协作开发
        提供可重复的数据库操作
    b.迁移文件结构
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            from django.db import migrations, models
            import django.db.models.deletion
            class Migration(migrations.Migration):
            from django.db import migrations, models
            class Migration(migrations.Migration):
            ---
    c.迁移命令详解
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            略

02.自动迁移生成
    a.字段添加
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            class Product(models.Model):
                name = models.CharField(max_length=200)
                price = models.DecimalField(max_digits=10, decimal_places=2)
                description = models.TextField()
                sku = models.CharField(max_length=50, unique=True)
                weight = models.FloatField(null=True)
                is_digital = models.BooleanField()
                tags = models.TextField()
            ---
    b.字段修改
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            class Product(models.Model):
                name = models.CharField(max_length=200)
                price = models.DecimalField(max_digits=12, decimal_places=2)
                description = models.TextField()
                sku = models.CharField(max_length=100)
            ---
    c.关系字段变更
        a.功能说明
            ForeignKey实现一对多关系,必须指定on_delete删除策略,支持related_name配置反向关系名称,
            可设置null和blank允许空值,提供CASCADE、SET_NULL、PROTECT等多种删除行为。
        b.代码示例
            ---
            class Product(models.Model):
                name = models.CharField(max_length=200)
                category = models.ForeignKey()
            ---

03.自定义迁移操作
    a.数据迁移
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            from django.db import migrations
            from django.db.models import F
            def populate_product_skus(apps, schema_editor):
            def reverse_populate_product_skus(apps, schema_editor):
            class Migration(migrations.Migration):
            def validate_product_data(apps, schema_editor):
            class Migration(migrations.Migration):
            ---
    b.SQL迁移
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            from django.db import migrations
            class Migration(migrations.Migration):
            ---
    c.条件迁移
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            from django.db import migrations
            def create_postgresql_view(apps, schema_editor):
            def create_mysql_view(apps, schema_editor):
            def create_sqlite_view(apps, schema_editor):
            class Migration(migrations.Migration):
            ---

04.迁移最佳实践
    a.迁移管理策略
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            略
    b.迁移测试
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            from django.test import TestCase
            from django.core.management import call_command
            from store.models import Product, Category
            class MigrationTestCase(TestCase):
                def setUp(self):
                def test_0002_add_product_stock(self):
                def test_0006_populate_product_skus(self):
            ---

3.4 QuerySet查询API

01.基础查询操作
    a.查询方法概览
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            from store.models import Product, Category
            ---
    b.查询过滤器
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            from datetime import date, timedelta
            from django.utils import timezone
            ---
    c.查询链式调用
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            from django.db.models import Count, Avg, Max, Min, Sum
            def search_products(query_text, min_price=None, max_price=None, category=None):
                return queryset.order_by('-created_at')
            ---

02.复杂查询技巧
    a.Q对象使用
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            from django.db.models import Q
            def build_filter_query(criteria):
                return query
            from django.db.models import Count
            ---
    b.F对象使用
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            from django.db.models import F
            from datetime import timedelta
            from django.utils import timezone
            def update_product_prices():
            ---
    c.聚合查询
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            from django.db.models import Avg, Count, Max, Min, Sum, StdDev, Variance
            from store.models import Product
            from django.db.models import Count
            from django.db.models import IntegerField
            from django.db.models.functions import Cast
            from django.db.models import Window
            from django.db.models.functions import RowNumber
            ---

03.查询优化技巧
    a.select_related优化
        a.功能说明
            ForeignKey实现一对多关系,必须指定on_delete删除策略,支持related_name配置反向关系名称,
            可设置null和blank允许空值,提供CASCADE、SET_NULL、PROTECT等多种删除行为。
        b.代码示例
            ---
            def get_products_with_details():
                return Product.objects.select_related(
            import time
            from django.db import connection
            def benchmark_queries():
            ---
    b.prefetch_related优化
        a.功能说明
            ForeignKey实现一对多关系,必须指定on_delete删除策略,支持related_name配置反向关系名称,
            可设置null和blank允许空值,提供CASCADE、SET_NULL、PROTECT等多种删除行为。
        b.代码示例
            ---
            from django.db.models import Prefetch
            from django.db.models import Prefetch
            def get_products_with_custom_relations():
                return Product.objects.prefetch_related(
            ---
    c.only和defer优化
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            def get_product_list(fields=None, exclude_fields=None):
                return queryset
            ---
    d.批量操作优化
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            from django.db import transaction
            def create_products_bulk(product_data):
                return created_products
            def update_prices_bulk(updates):
            def delete_inactive_products():
                return deleted_count
            import time
            from django.db import connection
                return queryset.iterator()
            return queryset.defer(*large_fields)
                return queryset.select_related('category')
            return queryset
            ---

3.5 高级查询技巧

01.子查询
    a.使用子查询进行复杂筛选
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            from django.db.models import Subquery, OuterRef
            from store.models import Product, Category, Order, OrderItem
            from django.db.models import Avg
            from django.db.models import OuterRef, Subquery
            from django.db.models import Exists
            from django.db.models import OuterRef, Max, Subquery
            def get_top_selling_products(limit=10):
                from django.db.models import Subquery, OuterRef, Sum
                from django.db.models import F
                return products_with_rank
            def get_cheaper_products():
                from django.db.models import Subquery, OuterRef, Avg
                return cheaper_products
            ---
    b.Window函数
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            from django.db.models import Window, F, RowNumber
            from django.db.models.functions import Lag, Lead
            from django.db.models.functions import DenseRank, Rank
            from django.db.models.functions import Avg
            def analyze_product_trends():
                return Product.objects.annotate(
            def get_paginated_analysis(page=1, page_size=20):
                return Product.objects.annotate(
            ---
    c.CTE(通用表表达式)
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            from django.db.models import With
            def get_category_statistics():
                from django.db.models import Sum, Max, Min, With
                return Category.objects.annotate(
            def get_category_tree():
                return Category.objects.filter(
            def perform_complex_analysis():
                return Product.objects.annotate(
            ---

02.原生SQL查询
    a.raw()方法
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            from django.db.models import IntegerField
            def get_products_by_price_range(min_price, max_price):
            ---
    b.extra()方法
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            def get_products_with_markup():
                return Product.objects.extra(
            def get_inventory_status():
                return Product.objects.extra(
            ---
    c.cursor()方法
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            from django.db import connection
            def execute_custom_query(sql, params=None):
                    return dict_results
            def build_product_query(filters=None):
                return sql, params
            def call_stored_procedure(procedure_name, params=None):
                    return cursor.fetchall()
            ---

03.查询性能分析
    a.查询计划分析
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            from django.db import connection
            def analyze_query(queryset):
                    return plan
            def monitor_query_performance(queryset, description=""):
                import time
                from django.db import connection
                return result, execution_time, query_count
            def get_product_analysis():
                from store.models import Product
                return {
                return slow_queries, optimization_tips
            ---
    b.查询优化策略
        a.功能说明
            该功能提供完整的实现示例,展示了核心配置和使用方法,包含必要的字段定义和业务逻辑处理。
        b.代码示例
            ---
            class QueryOptimizer:
                @staticmethod
                def optimize_queryset(queryset, context=None):
                    return queryset
                @staticmethod
                def _basic_optimization(queryset):
                        return queryset[:50]
                    return queryset
                @staticmethod
                def _optimize_for_list(queryset):
                    return queryset.iterator()
                    return queryset
                    return queryset
            class ProductService:
                    return QueryOptimizer.optimize_queryset(
                    return QueryOptimizer.optimize_queryset(
                    return QueryOptimizer.optimize_queryset(
                        return func(optimized_queryset, *args[1:], **kwargs)
                    return func(*args, **kwargs)
                return wrapper
                return Product.objects.all()
            class QueryBuilder:
                    return queryset
                return queryset
                return QueryOptimizer.optimize_queryset(queryset)
            ---

4 视图与路由

4.1 URL路由配置

01.URLconf基础
    a.URL模式定义
        a.功能说明
            urlpatterns列表定义URL路由规则,path()函数处理简单路径转换,
            re_path()函数处理正则表达式匹配,include()函数包含其他URL配置模块。
            Django的URL配置通过urlpatterns列表集中管理所有路由规则,支持路径参数、正则表达式匹配和模块化配置。
        b.代码示例
            ---
            # urls.py
            from django.contrib import admin
            from django.urls import path, include, re_path
            from apps.blog import views as blog_views

            urlpatterns = [
                # 管理后台
                path('admin/', admin.site.urls),

                # 首页
                path('', blog_views.home, name='home'),

                # 博客应用
                path('blog/', blog_views.post_list, name='blog_list'),
                path('blog/<int:year>/', blog_views.post_year, name='post_year'),
                path('blog/<int:year>/<int:month>/', blog_views.post_month, name='post_month'),
                path('blog/<int:year>/<int:month>/<slug:slug>/',
                     blog_views.post_detail, name='post_detail'),

                # 正则表达式URL
                re_path(r'^articles/(?P<year>\d{4})/$', blog_views.article_year),
                re_path(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$',
                        blog_views.article_month),

                # 包含其他URL配置
                path('users/', include('apps.users.urls')),
                path('api/', include('apps.api.urls')),
            ]
            ---
    b.路径转换器
        a.功能说明
            Django内置路径转换器包括str(匹配非空字符串)、int(匹配0或正整数)、slug(匹配字母数字连字符下划线)、uuid(匹配UUID格式)、
            path(匹配包含路径分隔符的字符串)。可通过定义包含regex、to_python()和to_url()方法的类来创建自定义转换器,
            使用register_converter()注册后即可在URL模式中使用。
        b.代码示例
            ---
            # converters.py
            from django.urls.converters import register_converter

            class YearConverter:
                regex = r'\d{4}'

                def to_python(self, value):
                    return int(value)

                def to_url(self, value):
                    return str(value)

            class PositiveIntConverter:
                regex = r'[1-9]\d*'

                def to_python(self, value):
                    return int(value)

                def to_url(self, value):
                    return str(value)

            # 注册转换器
            register_converter(YearConverter, 'yyyy')
            register_converter(PositiveIntConverter, 'positive')

            # urls.py中使用
            urlpatterns = [
                path('archive/<yyyy:year>/', views.archive_year),
                path('post/<positive:post_id>/', views.post_detail),
            ]
            ---
    c.命名URL和反向解析
        a.功能说明
            使用name参数为URL模式命名,通过reverse()函数或{% url %}模板标签根据名称生成URL,
            命名空间机制避免不同应用间的URL名称冲突。反向解析使代码与URL结构解耦,修改URL模式时无需更改视图和模板中的硬编码路径。
        b.代码示例
            ---
            # urls.py
            urlpatterns = [
                path('blog/<int:post_id>/', blog_views.post_detail, name='post_detail'),
                path('blog/<int:post_id>/edit/', blog_views.post_edit, name='post_edit'),
                path('blog/<int:post_id>/delete/', blog_views.post_delete, name='post_delete'),
            ]

            # views.py中使用reverse()
            from django.urls import reverse
            from django.shortcuts import redirect

            def post_create(request):
                if request.method == 'POST':
                    # 创建文章逻辑
                    post = create_post_from_form(request.POST)
                    return redirect('post_detail', post_id=post.id)

            def post_delete(request, post_id):
                post = get_object_or_404(Post, pk=post_id)
                post.delete()
                return redirect('post_list')

            # 模板中使用{% url %}
            """
            <a href="{% url 'post_detail' post.id %}">{{ post.title }}</a>
            <a href="{% url 'post_edit' post.id %}">编辑</a>
            <a href="{% url 'post_delete' post.id %}">删除</a>
            """
            ---

02.高级URL配置
    a.命名空间
        a.功能说明
            应用命名空间通过app_name变量定义,实例命名空间通过include()的namespace参数指定,支持嵌套命名空间构建复杂URL结构。
            命名空间使用冒号分隔(如'blog:detail'),在多应用项目中有效避免URL名称冲突,提高代码可维护性。
        b.代码示例
            ---
            # blog/urls.py
            app_name = 'blog'
            urlpatterns = [
                path('', views.post_list, name='list'),
                path('<int:pk>/', views.post_detail, name='detail'),
                path('create/', views.post_create, name='create'),
            ]

            # project/urls.py
            from django.urls import path, include

            urlpatterns = [
                path('blog/', include('blog.urls', namespace='blog')),
                path('news/', include('news.urls', namespace='news')),
            ]

            # 视图中使用命名空间URL
            from django.urls import reverse

            def some_view(request):
                blog_url = reverse('blog:list')  # /blog/
                news_url = reverse('news:detail', args=[1])  # /news/1/

            # 模板中使用命名空间URL
            """
            <a href="{% url 'blog:detail' post.pk %}">{{ post.title }}</a>
            <a href="{% url 'news:list' %}">所有新闻</a>
            """
            ---
    b.URL参数传递
        a.功能说明
            URL参数通过路径转换器或正则表达式命名组捕获并传递给视图函数,可使用字典向视图传递额外参数,
            查询参数通过request.GET获取。参数传递支持位置参数和关键字参数两种方式,视图函数通过参数名接收URL中捕获的值。
        b.代码示例
            ---
            # urls.py
            urlpatterns = [
                # 路径转换器参数
                path('user/<int:user_id>/', views.user_profile),
                path('post/<slug:slug>/', views.post_detail),

                # 正则表达式参数
                re_path(r'^category/(?P<category_name>\w+)/$', views.category_posts),

                # 额外参数
                path('archive/', views.archive, {'show_all': False}),
            ]

            # views.py
            def user_profile(request, user_id):
                user = get_object_or_404(User, pk=user_id)
                return render(request, 'users/profile.html', {'user': user})

            def post_detail(request, slug):
                post = get_object_or_404(Post, slug=slug)
                return render(request, 'blog/detail.html', {'post': post})

            def category_posts(request, category_name):
                posts = Post.objects.filter(category__name=category_name)
                return render(request, 'blog/category.html', {
                    'category_name': category_name,
                    'posts': posts
                })

            def archive(request, show_all):
                if show_all:
                    posts = Post.objects.all()
                else:
                    posts = Post.objects.filter(published=True)
                return render(request, 'blog/archive.html', {'posts': posts})

            # 查询参数处理
            def search_posts(request):
                query = request.GET.get('q', '')
                category = request.GET.get('category', '')

                posts = Post.objects.all()
                if query:
                    posts = posts.filter(title__icontains=query)
                if category:
                    posts = posts.filter(category__name=category)

                return render(request, 'blog/search.html', {
                    'posts': posts,
                    'query': query,
                    'category': category
                })
            ---
    c.动态URL生成
        a.功能说明
            通过reverse()函数和QueryDict类程序化构建URL,支持参数验证、URL格式化和复杂查询参数处理。
            动态URL生成常用于分页、搜索过滤等场景,可根据业务逻辑灵活组装URL参数,保持代码的可维护性和灵活性。
        b.代码示例
            ---
            # utils.py
            from django.urls import reverse
            django.http import QueryDict

            def build_url_with_params(url_name, page=1, **kwargs):
                """构建带查询参数的URL"""
                base_url = reverse(url_name, kwargs=kwargs)

                if page > 1:
                    query_dict = QueryDict(mutable=True)
                    query_dict['page'] = page
                    return f"{base_url}?{query_dict.urlencode()}"

                return base_url

            def build_search_url(query, category=None, page=1):
                """构建搜索URL"""
                params = {'q': query}
                if category:
                    params['category'] = category
                if page > 1:
                    params['page'] = page

                query_string = '&'.join([f"{k}={v}" for k, v in params.items()])
                return f"/search/?{query_string}"

            # 视图中使用
            from django.core.paginator import Paginator

            def post_list(request):
                posts = Post.objects.all()
                paginator = Paginator(posts, 10)
                page = request.GET.get('page', 1)
                posts_page = paginator.get_page(page)

                # 构建分页URL
                pagination_urls = []
                for page_num in paginator.page_range:
                    url = build_url_with_params('post_list', page=page_num)
                    pagination_urls.append({
                        'number': page_num,
                        'url': url,
                        'is_current': page_num == posts_page.number
                    })

                return render(request, 'blog/list.html', {
                    'posts': posts_page,
                    'pagination_urls': pagination_urls
                })
            ---

03.错误处理和重定向
    a.自定义错误页面
        a.功能说明
            通过handler404、handler500、handler403、handler400变量指定自定义错误处理视图,
            分别处理页面未找到、服务器错误、权限错误和请求错误。
            自定义错误页面提供友好的用户体验,可包含导航链接、错误说明和品牌元素。
        b.代码示例
            ---
            # urls.py
            from django.urls import path
            from . import views

            handler404 = 'blog.views.custom_404'
            handler500 = 'blog.views.custom_500'
            handler403 = 'blog.views.custom_403'
            handler400 = 'blog.views.custom_400'

            # views.py
            from django.shortcuts import render
            from django.http import HttpResponseNotFound, HttpResponseServerError

            def custom_404(request, exception):
                return render(request, 'errors/404.html', status=404)

            def custom_500(request):
                return render(request, 'errors/500.html', status=500)

            def custom_403(request, exception):
                return render(request, 'errors/403.html', status=403)

            def custom_400(request, exception):
                return render(request, 'errors/400.html', status=400)

            # 模板示例 templates/errors/404.html
            """
            <!DOCTYPE html>
            <html>
            <head>
                <title>页面未找到 - 404</title>
            </head>
            <body>
                <h1>页面未找到</h1>
                <p>抱歉,您访问的页面不存在。</p>
                <p><a href="{% url 'home' %}">返回首页</a></p>
            </body>
            </html>
            """
            ---
    b.URL重定向
        a.功能说明
            使用redirect()函数实现临时重定向,permanent参数设为True实现永久重定向,
            RedirectView类视图提供声明式重定向配置。重定向支持基于条件的动态跳转,可根据用户状态、权限或业务逻辑选择目标URL。
        b.代码示例
            ---
            from django.shortcuts import redirect, get_object_or_404
            from django.urls import reverse
            from django.views.generic import RedirectView

            # 函数视图重定向
            def old_post_detail(request, post_id):
                """旧的文章详情页,重定向到新的URL结构"""
                post = get_object_or_404(Post, pk=post_id)
                return redirect('post_detail', slug=post.slug, permanent=True)

            def login_redirect(request):
                """根据用户类型重定向到不同页面"""
                if request.user.is_authenticated:
                    if request.user.is_staff:
                        return redirect('admin:index')
                    else:
                        return redirect('user_dashboard')
                else:
                    return redirect('login')

            # 类视图重定向
            class PostRedirectView(RedirectView):
                permanent = False
                query_string = True
                pattern_name = 'post_detail'

                def get_redirect_url(self, *args, **kwargs):
                    post = get_object_or_404(Post, pk=kwargs['post_id'])
                    kwargs.update({'slug': post.slug})
                    return super().get_redirect_url(*args, **kwargs)

            # URL配置
            urlpatterns = [
                # 旧URL重定向到新URL
                path('old-post/<int:post_id>/', RedirectView.as_view(
                    url='/post/%(post_id)s/',
                    permanent=True
                )),

                # 基于模式的URL重定向
                path('blog/post/<int:post_id>/', RedirectView.as_view(
                    pattern_name='post_detail',
                    permanent=True
                )),
            ]
            ---
    c.中间件级别的URL处理
        a.功能说明
            通过自定义中间件实现URL重写、请求拦截和响应后处理,在请求到达视图前或响应返回前执行全局URL处理逻辑。
            中间件可处理URL尾部斜杠、多语言路径前缀、域名路由等跨应用的URL需求。
        b.代码示例
            ---
            # middleware.py
            from django.http import HttpResponsePermanentRedirect
            from django.urls import resolve

            class URLTrailingSlashMiddleware:
                """处理URL尾部斜杠的中间件"""
                def __init__(self, get_response):
                    self.get_response = get_response

                def __call__(self, request):
                    # 移除末尾斜杠(Django默认处理)
                    if request.path.endswith('/') and len(request.path) > 1:
                        new_path = request.path.rstrip('/')
                        if not self.is_static_file(new_path):
                            return HttpResponsePermanentRedirect(new_path)

                    return self.get_response(request)

                def is_static_file(self, path):
                    """检查是否为静态文件"""
                    static_extensions = ['.css', '.js', '.png', '.jpg', '.gif']
                    return any(path.endswith(ext) for ext in static_extensions)

            class LocaleURLMiddleware:
                """本地化URL中间件"""
                def __init__(self, get_response):
                    self.get_response = get_response

                def __call__(self, request):
                    # 从URL中提取语言代码
                    path_parts = request.path.split('/')
                    if len(path_parts) > 1 and path_parts[1] in ['en', 'zh', 'fr']:
                        request.LANGUAGE_CODE = path_parts[1]
                        # 移除语言前缀继续处理
                        request.path_info = '/' + '/'.join(path_parts[2:])

                    response = self.get_response(request)
                    return response

            # settings.py
            MIDDLEWARE = [
                'django.middleware.security.SecurityMiddleware',
                'myapp.middleware.LocaleURLMiddleware',
                'django.contrib.sessions.middleware.SessionMiddleware',
                'django.middleware.common.CommonMiddleware',
                'django.middleware.csrf.CsrfViewMiddleware',
                'django.contrib.auth.middleware.AuthenticationMiddleware',
                'myapp.middleware.URLTrailingSlashMiddleware',
                'django.contrib.messages.middleware.MessageMiddleware',
            ]
            ---

4.2 函数视图

01.基础函数视图
    a.视图函数定义
        a.功能说明
            视图函数接收HttpRequest对象作为第一个参数,返回HttpResponse对象,负责处理HTTP方法和请求参数。
            视图函数是Django处理请求的核心,通过render()渲染模板、redirect()重定向、JsonResponse()返回JSON等方式生成响应。
        b.代码示例
            ---
            from django.shortcuts import render, redirect, get_object_or_404
            from django.http import HttpResponse, JsonResponse, Http404
            from .models import Post, Category
            from .forms import PostForm

            def home_view(request):
                """首页视图"""
                latest_posts = Post.objects.order_by('-created_at')[:5]
                categories = Category.objects.all()

                context = {
                    'latest_posts': latest_posts,
                    'categories': categories,
                    'page_title': '欢迎来到我的博客'
                }
                return render(request, 'blog/home.html', context)

            def post_list_view(request):
                """文章列表视图"""
                posts = Post.objects.filter(published=True)

                # 处理分类过滤
                category_id = request.GET.get('category')
                if category_id:
                    posts = posts.filter(category_id=category_id)

                # 处理搜索
                search_query = request.GET.get('q')
                if search_query:
                    posts = posts.filter(title__icontains=search_query)

                context = {
                    'posts': posts,
                    'category_id': category_id,
                    'search_query': search_query
                }
                return render(request, 'blog/post_list.html', context)

            def post_detail_view(request, post_id):
                """文章详情视图"""
                try:
                    post = Post.objects.get(pk=post_id, published=True)
                except Post.DoesNotExist:
                    raise Http404("文章不存在")

                # 增加浏览次数
                post.views_count += 1
                post.save(update_fields=['views_count'])

                # 获取相关文章
                related_posts = Post.objects.filter(
                    category=post.category
                ).exclude(pk=post.pk)[:3]

                context = {
                    'post': post,
                    'related_posts': related_posts
                }
                return render(request, 'blog/post_detail.html', context)
            ---
    b.HTTP方法处理
        a.功能说明
            通过request.method检查请求方法类型,使用require_http_methods、require_GET、require_POST等装饰器限制允许的HTTP方法。
            不同HTTP方法对应不同的业务逻辑,GET用于查询数据,POST用于提交表单,PUT/DELETE用于RESTful API操作。
        b.代码示例
            ---
            from django.views.decorators.http import require_http_methods, require_GET, require_POST
            from django.contrib.auth.decorators import login_required

            @require_GET
            def post_create_view(request):
                """创建文章表单页面"""
                if not request.user.is_authenticated:
                    return redirect('login')

                form = PostForm()
                return render(request, 'blog/post_create.html', {'form': form})

            @require_http_methods(["GET", "POST"])
            @login_required
            def post_edit_view(request, post_id):
                """编辑文章视图"""
                post = get_object_or_404(Post, pk=post_id, author=request.user)

                if request.method == 'GET':
                    form = PostForm(instance=post)
                    return render(request, 'blog/post_edit.html', {
                        'form': form,
                        'post': post
                    })

                elif request.method == 'POST':
                    form = PostForm(request.POST, request.FILES, instance=post)
                    if form.is_valid():
                        post = form.save(commit=False)
                        post.updated_at = timezone.now()
                        post.save()
                        return redirect('post_detail', post_id=post.id)
                    else:
                        return render(request, 'blog/post_edit.html', {
                            'form': form,
                            'post': post
                        })

            @require_POST
            def post_delete_view(request, post_id):
                """删除文章视图"""
                post = get_object_or_404(Post, pk=post_id, author=request.user)

                # CSRF保护
                if not request.POST.get('confirm'):
                    return render(request, 'blog/post_delete_confirm.html', {
                        'post': post
                    })

                post.delete()
                return redirect('post_list')

            def api_like_post_view(request, post_id):
                """点赞文章API视图"""
                if request.method == 'POST':
                    post = get_object_or_404(Post, pk=post_id)

                    if request.user.is_authenticated:
                        # 处理用户点赞逻辑
                        if post.likes.filter(pk=request.user.pk).exists():
                            post.likes.remove(request.user)
                            liked = False
                        else:
                            post.likes.add(request.user)
                            liked = True

                        return JsonResponse({
                            'success': True,
                            'liked': liked,
                            'likes_count': post.likes.count()
                        })
                    else:
                        return JsonResponse({
                            'success': False,
                            'error': '需要登录'
                        }, status=401)

                # 其他方法不支持
                return JsonResponse({
                    'success': False,
                    'error': '不支持的请求方法'
                }, status=405)
            ---
    c.请求参数处理
        a.功能说明
            GET参数从request.GET获取,POST参数从request.POST获取,文件上传从request.FILES获取,请求元数据从request.META获取。
            参数处理包括数据验证、类型转换、默认值设置,支持复杂的查询过滤、分页和排序逻辑。
        b.代码示例
            ---
            from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
            from django.db.models import Q
            import json

            def advanced_search_view(request):
                """高级搜索视图"""
                # GET参数处理
                query = request.GET.get('q', '').strip()
                category_id = request.GET.get('category')
                date_from = request.GET.get('date_from')
                date_to = request.GET.get('date_to')
                sort_by = request.GET.get('sort', 'latest')
                page = request.GET.get('page', 1)

                # 构建查询
                posts = Post.objects.filter(published=True)

                if query:
                    posts = posts.filter(
                        Q(title__icontains=query) |
                        Q(content__icontains=query) |
                        Q(author__username__icontains=query)
                    )

                if category_id:
                    posts = posts.filter(category_id=category_id)

                if date_from:
                    posts = posts.filter(created_at__date__gte=date_from)

                if date_to:
                    posts = posts.filter(created_at__date__lte=date_to)

                # 排序
                sort_options = {
                    'latest': '-created_at',
                    'oldest': 'created_at',
                    'popular': '-views_count',
                    'title': 'title'
                }

                if sort_by in sort_options:
                    posts = posts.order_by(sort_options[sort_by])

                # 分页处理
                paginator = Paginator(posts, 10)
                try:
                    posts_page = paginator.get_page(page)
                except PageNotAnInteger:
                    posts_page = paginator.get_page(1)
                except EmptyPage:
                    posts_page = paginator.get_page(paginator.num_pages)

                # 构建搜索上下文
                context = {
                    'posts': posts_page,
                    'paginator': paginator,
                    'search_params': {
                        'q': query,
                        'category': category_id,
                        'date_from': date_from,
                        'date_to': date_to,
                        'sort': sort_by
                    }
                }

                return render(request, 'blog/advanced_search.html', context)

            def json_api_view(request):
                """JSON API视图"""
                if request.method == 'POST':
                    try:
                        # 解析JSON数据
                        data = json.loads(request.body)
                        title = data.get('title')
                        content = data.get('content')

                        # 验证数据
                        if not title or not content:
                            return JsonResponse({
                                'success': False,
                                'error': '标题和内容不能为空'
                            }, status=400)

                        # 创建文章
                        post = Post.objects.create(
                            title=title,
                            content=content,
                            author=request.user
                        )

                        return JsonResponse({
                            'success': True,
                            'post_id': post.id,
                            'message': '文章创建成功'
                        })

                    except json.JSONDecodeError:
                        return JsonResponse({
                            'success': False,
                            'error': '无效的JSON数据'
                        }, status=400)

                # GET请求返回文章列表
                posts = Post.objects.filter(published=True)
                posts_data = [{
                    'id': post.id,
                    'title': post.title,
                    'author': post.author.username,
                    'created_at': post.created_at.isoformat()
                } for post in posts]

                return JsonResponse({
                    'success': True,
                    'posts': posts_data
                })
            ---

02.装饰器应用
    a.认证和权限装饰器
        a.功能说明
            login_required装饰器要求用户登录,user_passes_test执行自定义用户测试,permission_required检查用户权限。
            认证装饰器在视图执行前验证用户身份和权限,未通过验证时自动重定向到登录页面或返回403错误。
        b.代码示例
            ---
            from django.contrib.auth.decorators import login_required, user_passes_test
            from django.contrib.auth.mixins import UserPassesTestMixin
            from django.contrib.auth.models import User

            def is_author(user):
                """检查用户是否为作者角色"""
                return user.is_authenticated and user.groups.filter(name='authors').exists()

            def is_staff_user(user):
                """检查用户是否为管理员"""
                return user.is_authenticated and user.is_staff

            @login_required(login_url='/login/')
            def user_dashboard_view(request):
                """用户仪表板"""
                user_posts = Post.objects.filter(author=request.user)
                user_comments = Comment.objects.filter(user=request.user)

                context = {
                    'user_posts': user_posts,
                    'user_comments': user_comments,
                    'posts_count': user_posts.count(),
                    'comments_count': user_comments.count()
                }
                return render(request, 'users/dashboard.html', context)

            @user_passes_test(is_author, login_url='/login/')
            @login_required
            def author_tools_view(request):
                """作者工具页面"""
                published_posts = Post.objects.filter(author=request.user, published=True)
                draft_posts = Post.objects.filter(author=request.user, published=False)

                context = {
                    'published_posts': published_posts,
                    'draft_posts': draft_posts,
                    'total_views': sum(post.views_count for post in published_posts)
                }
                return render(request, 'authors/tools.html', context)

            @user_passes_test(is_staff_user, login_url='/login/')
            def admin_statistics_view(request):
                """管理员统计页面"""
                from django.db.models import Count, Sum

                stats = {
                    'total_users': User.objects.count(),
                    'total_posts': Post.objects.count(),
                    'total_comments': Comment.objects.count(),
                    'total_views': Post.objects.aggregate(total=Sum('views_count'))['total'] or 0,
                    'posts_by_category': Post.objects.values('category__name').annotate(count=Count('id'))
                }

                return render(request, 'admin/statistics.html', {'stats': stats})

            # 自定义装饰器
            def post_author_required(view_func):
                """要求用户为文章作者"""
                def wrapper(request, *args, **kwargs):
                    post_id = kwargs.get('post_id')
                    post = get_object_or_404(Post, pk=post_id)

                    if post.author != request.user:
                        return HttpResponseForbidden("您没有权限访问此页面")

                    return view_func(request, *args, **kwargs)
                return wrapper

            @post_author_required
            def edit_own_post_view(request, post_id):
                """编辑自己的文章"""
                post = get_object_or_404(Post, pk=post_id)
                # 编辑逻辑
                return render(request, 'blog/edit_post.html', {'post': post})
            ---
    b.HTTP方法和缓存装饰器
        a.功能说明
            require_http_methods限制允许的HTTP方法,cache_page实现页面级缓存,vary_on_headers根据请求头变化缓存不同版本。
            缓存装饰器显著提升响应速度,减少数据库查询和计算开销,适用于内容相对稳定的页面。
        b.代码示例
            ---
            from django.views.decorators.http import require_GET, require_POST, require_http_methods
            from django.views.decorators.cache import cache_page, never_cache, vary_on_cookie
            from django.views.decorators.vary import vary_on_headers
            from django.utils.decorators import method_decorator
            from django.core.cache import cache
            import time

            @cache_page(60 * 15)  # 缓存15分钟
            @vary_on_cookie
            def popular_posts_view(request):
                """热门文章页面(缓存)"""
                popular_posts = Post.objects.filter(
                    published=True
                ).order_by('-views_count')[:10]

                return render(request, 'blog/popular_posts.html', {
                    'posts': popular_posts,
                    'cached_at': time.time()
                })

            @vary_on_headers('User-Agent')
            @cache_page(60 * 5)  # 缓存5分钟
            def home_page_view(request):
                """首页(根据User-Agent变化缓存)"""
                featured_posts = Post.objects.filter(featured=True, published=True)
                recent_posts = Post.objects.order_by('-created_at')[:5]

                return render(request, 'blog/home.html', {
                    'featured_posts': featured_posts,
                    'recent_posts': recent_posts
                })

            @never_cache  # 禁止缓存
            def user_profile_view(request):
                """用户资料页(禁止缓存)"""
                if not request.user.is_authenticated:
                    return redirect('login')

                return render(request, 'users/profile.html')

            @require_GET
            def static_info_view(request):
                """静态信息页面(仅GET请求)"""
                return render(request, 'info/about.html')

            @require_http_methods(["GET", "POST"])
            def contact_view(request):
                """联系我们页面"""
                if request.method == 'POST':
                    # 处理联系表单提交
                    name = request.POST.get('name')
                    email = request.POST.get('email')
                    message = request.POST.get('message')

                    # 发送邮件或保存消息
                    # ...

                    return render(request, 'contact/success.html')

                return render(request, 'contact/form.html')

            # 自定义缓存装饰器
            def custom_cache(cache_timeout=300):
                """自定义缓存装饰器"""
                def decorator(view_func):
                    def wrapper(request, *args, **kwargs):
                        # 生成缓存键
                        cache_key = f"{view_func.__name__}:{request.path}:{request.GET.urlencode()}"

                        # 尝试从缓存获取
                        cached_response = cache.get(cache_key)
                        if cached_response:
                            return cached_response

                        # 执行视图并缓存结果
                        response = view_func(request, *args, **kwargs)
                        cache.set(cache_key, response, cache_timeout)

                        return response
                    return wrapper
                return decorator

            @custom_cache(cache_timeout=60 * 10)
            def trending_topics_view(request):
                """热门话题页面(自定义缓存)"""
                # 计算热门话题的逻辑
                topics = calculate_trending_topics()

                return render(request, 'blog/trending_topics.html', {
                    'topics': topics
                })
            ---
    c.请求处理装饰器
        a.功能说明
            gzip_page压缩响应内容减少传输大小,csrf_protect启用CSRF保护,ensure_csrf_cookie确保CSRF cookie存在,
            xframe_options_exempt允许页面被iframe嵌入。这些装饰器处理安全、性能和兼容性需求,可灵活组合使用。
        b.代码示例
            ---
            from django.views.decorators.gzip import gzip_page
            from django.views.decorators.csrf import csrf_protect, ensure_csrf_cookie, csrf_exempt
            from django.views.decorators.clickjacking import xframe_options_exempt
            from django.views.decorators.debug import sensitive_post_parameters, sensitive_variables
            import json

            @gzip_page
            @cache_page(60 * 30)  # 压缩并缓存30分钟
            def large_content_view(request):
                """大内容页面(启用gzip压缩)"""
                # 生成大量内容
                large_data = generate_large_data()

                return render(request, 'blog/large_content.html', {
                    'large_data': large_data
                })

            @csrf_protect
            def secure_form_view(request):
                """安全表单页面(CSRF保护)"""
                if request.method == 'POST':
                    # 处理表单数据
                    title = request.POST.get('title')
                    content = request.POST.get('content')

                    # CSRF保护已启用
                    # 表单处理逻辑
                    return redirect('form_success')

                return render(request, 'forms/secure_form.html')

            @ensure_csrf_cookie
            def api_csrf_view(request):
                """API视图(确保CSRF cookie)"""
                if request.method == 'GET':
                    # 返回包含CSRF token的响应
                    return JsonResponse({
                        'message': 'CSRF cookie已设置'
                    })

            @csrf_exempt
            def webhook_view(request):
                """Webhook接收端(免除CSRF保护)"""
                if request.method == 'POST':
                    try:
                        data = json.loads(request.body)
                        # 处理webhook数据
                        process_webhook_data(data)

                        return JsonResponse({'status': 'success'})
                    except Exception as e:
                        return JsonResponse({
                            'status': 'error',
                            'message': str(e)
                        }, status=400)

                return JsonResponse({'status': 'ok'})

            @xframe_options_exempt
            def embed_content_view(request):
                """可嵌入内容页面(免除X-Frame-Options)"""
                return render(request, 'embed/content.html')

            @sensitive_post_parameters('password', 'credit_card')
            def sensitive_form_view(request):
                """敏感表单处理"""
                if request.method == 'POST':
                    password = request.POST.get('password')
                    credit_card = request.POST.get('credit_card')

                    # Django将在错误报告中隐藏这些敏感参数
                    # 处理逻辑
                    return redirect('success')

                return render(request, 'forms/sensitive_form.html')

            @sensitive_variables('api_key', 'secret')
            def api_call_view(request):
                """API调用视图(敏感变量)"""
                api_key = 'super_secret_api_key'
                secret = 'top_secret_code'

                # 这些变量在错误报告中将被隐藏
                result = make_api_call(api_key, secret)

                return JsonResponse({'result': result})
            ---

4.3 类视图(CBV)

01.基础类视图
    a.View基类
        a.功能说明
            Django.views.View是所有类视图的基类,支持HTTP方法自动分发,将GET/POST等请求分发到对应的get()/post()方法处理。
            View基类提供dispatch()方法控制请求分发流程,支持装饰器和Mixin扩展功能。
        b.代码示例
            ---
            from django.views import View
            from django.shortcuts import render, redirect, get_object_or_404
            from django.contrib.auth.decorators import login_required
            from django.utils.decorators import method_decorator
            from django.http import JsonResponse
            from .models import Post
            from .forms import PostForm

            class PostCreateView(View):
                """创建文章的类视图"""

                @method_decorator(login_required)
                def dispatch(self, request, *args, **kwargs):
                    # 调用父类dispatch方法
                    return super().dispatch(request, *args, **kwargs)

                def get(self, request):
                    """处理GET请求,显示创建表单"""
                    form = PostForm()
                    return render(request, 'blog/post_create.html', {'form': form})

                def post(self, request):
                    """处理POST请求,创建文章"""
                    form = PostForm(request.POST, request.FILES)
                    if form.is_valid():
                        post = form.save(commit=False)
                        post.author = request.user
                        post.save()
                        return redirect('post_detail', pk=post.pk)

                    return render(request, 'blog/post_create.html', {'form': form})

            class PostUpdateView(View):
                """更新文章的类视图"""

                def get_object(self, pk):
                    """获取文章对象"""
                    return get_object_or_404(Post, pk=pk, author=self.request.user)

                def get(self, request, pk):
                    """显示编辑表单"""
                    post = self.get_object(pk)
                    form = PostForm(instance=post)
                    return render(request, 'blog/post_edit.html', {
                        'form': form,
                        'post': post
                    })

                def post(self, request, pk):
                    """处理文章更新"""
                    post = self.get_object(pk)
                    form = PostForm(request.POST, request.FILES, instance=post)
                    if form.is_valid():
                        updated_post = form.save(commit=False)
                        updated_post.updated_at = timezone.now()
                        updated_post.save()
                        return redirect('post_detail', pk=updated_post.pk)

                    return render(request, 'blog/post_edit.html', {
                        'form': form,
                        'post': post
                    })

            class PostDeleteView(View):
                """删除文章的类视图"""

                def get_object(self, pk):
                    """获取文章对象"""
                    return get_object_or_404(Post, pk=pk, author=self.request.user)

                def get(self, request, pk):
                    """显示删除确认页面"""
                    post = self.get_object(pk)
                    return render(request, 'blog/post_delete.html', {'post': post})

                def post(self, request, pk):
                    """处理删除操作"""
                    post = self.get_object(pk)
                    post.delete()
                    return redirect('post_list')

            class PostListView(View):
                """文章列表类视图"""

                def get_queryset(self):
                    """获取查询集"""
                    return Post.objects.filter(published=True)

                def get_context_data(self):
                    """获取上下文数据"""
                    queryset = self.get_queryset()

                    # 处理搜索查询
                    search_query = self.request.GET.get('q')
                    if search_query:
                        queryset = queryset.filter(title__icontains=search_query)

                    # 处理分类过滤
                    category_id = self.request.GET.get('category')
                    if category_id:
                        queryset = queryset.filter(category_id=category_id)

                    return {
                        'posts': queryset,
                        'search_query': search_query or '',
                        'category_id': category_id or ''
                    }

                def get(self, request):
                    """处理GET请求"""
                    context = self.get_context_data()
                    return render(request, 'blog/post_list.html', context)
            ---
    b.TemplateView
        a.功能说明
            TemplateView用于渲染模板并返回HTML响应,通过template_name指定模板,通过get_context_data()添加上下文数据。
            适用于静态页面、关于页面、联系页面等不需要复杂数据处理的场景,支持缓存和模板继承。
        b.代码示例
            ---
            from django.views.generic.base import TemplateView
            from django.views.decorators.cache import cache_page
            from django.utils.decorators import method_decorator
            from .models import Post, Category

            @method_decorator(cache_page(60 * 15))  # 缓存15分钟
            class HomePageView(TemplateView):
                """首页模板视图"""
                template_name = 'blog/home.html'

                def get_context_data(self, **kwargs):
                    context = super().get_context_data(**kwargs)

                    # 添加最新文章
                    context['latest_posts'] = Post.objects.filter(
                        published=True
                    ).order_by('-created_at')[:6]

                    # 添加分类
                    context['categories'] = Category.objects.all()[:8]

                    # 添加推荐文章
                    context['featured_posts'] = Post.objects.filter(
                        featured=True,
                        published=True
                    ).order_by('-created_at')[:3]

                    context['page_title'] = '欢迎来到我的博客'
                    return context

            class AboutView(TemplateView):
                """关于我们页面"""
                template_name = 'info/about.html'

                def get_context_data(self, **kwargs):
                    context = super().get_context_data(**kwargs)
                    context['page_title'] = '关于我们'
                    context['team_members'] = [
                        {'name': '张三', 'role': '技术总监'},
                        {'name': '李四', 'role': '产品经理'},
                        {'name': '王五', 'role': '设计师'},
                    ]
                    return context

            class ContactView(TemplateView):
                """联系我们页面"""
                template_name = 'info/contact.html'

                def get_context_data(self, **kwargs):
                    context = super().get_context_data(**kwargs)
                    context['contact_info'] = {
                        'email': '[email protected]',
                        'phone': '+86 123 4567 8900',
                        'address': '北京市朝阳区xxx街道xxx号'
                    }
                    return context

                def post(self, request):
                    """处理联系表单提交"""
                    name = request.POST.get('name')
                    email = request.POST.get('email')
                    message = request.POST.get('message')

                    # 验证表单数据
                    if not all([name, email, message]):
                        context = self.get_context_data()
                        context['error'] = '请填写所有必填字段'
                        context['form_data'] = request.POST
                        return self.render_to_response(context)

                    # 发送邮件或保存消息
                    # send_contact_email(name, email, message)

                    context = self.get_context_data()
                    context['success'] = True
                    return self.render_to_response(context)

            class ErrorPageView(TemplateView):
                """错误页面视图"""

                def get_template_names(self):
                    """根据参数选择模板"""
                    error_type = self.kwargs.get('error_type', '404')
                    return f'errors/{error_type}.html'

                def get_context_data(self, **kwargs):
                    context = super().get_context_data(**kwargs)
                    context['error_type'] = self.kwargs.get('error_type', '404')
                    return context

            # URL配置
            urlpatterns = [
                path('', HomePageView.as_view(), name='home'),
                path('about/', AboutView.as_view(), name='about'),
                path('contact/', ContactView.as_view(), name='contact'),
                path('error/<str:error_type>/', ErrorPageView.as_view(), name='error_page'),
            ]
            ---
    c.RedirectView
        a.功能说明
            RedirectView实现URL重定向功能,permanent属性控制永久或临时重定向,pattern_name指定目标URL模式,url属性设置硬编码URL。
            通过get_redirect_url()方法动态生成重定向目标,支持查询参数保留和条件重定向。
        b.代码示例
            ---
            from django.views.generic.base import RedirectView
            from django.shortcuts import get_object_or_404
            from django.urls import reverse
            from .models import Post

            class PostRedirectView(RedirectView):
                """文章重定向视图"""
                permanent = False  # 临时重定向
                query_string = True  # 保留查询参数
                pattern_name = 'post_detail'  # URL模式名称

                def get_redirect_url(self, *args, **kwargs):
                    """获取重定向URL"""
                    # 获取文章对象
                    post = get_object_or_404(Post, pk=kwargs['post_id'])

                    # 使用文章的slug作为新URL参数
                    kwargs['slug'] = post.slug
                    return super().get_redirect_url(*args, **kwargs)

            class CategoryRedirectView(RedirectView):
                """分类重定向视图"""
                permanent = True  # 永久重定向

                def get_redirect_url(self, *args, **kwargs):
                    """根据分类名称重定向"""
                    category_slug = kwargs.get('category_slug')
                    if category_slug:
                        return reverse('category_posts', kwargs={'slug': category_slug})
                    return reverse('home')

            class ExternalRedirectView(RedirectView):
                """外部URL重定向视图"""
                url = 'https://example.com'
                permanent = False

                def get_redirect_url(self, *args, **kwargs):
                    """可配置的外部重定向"""
                    target_url = self.request.GET.get('url', self.url)

                    # 简单的URL验证
                    if target_url.startswith(('http://', 'https://')):
                        return target_url

                    return reverse('home')

            class DomainRedirectView(RedirectView):
                """域名重定向视图"""
                permanent = False

                def get_redirect_url(self, *args, **kwargs):
                    """根据域名重定向到不同页面"""
                    domain = self.request.get_host()

                    if 'mobile' in domain:
                        return reverse('mobile_home')
                    elif 'admin' in domain:
                        return reverse('admin:index')
                    else:
                        return reverse('desktop_home')

            # URL配置
            urlpatterns = [
                # 旧URL重定向到新URL
                path('post/<int:post_id>/', PostRedirectView.as_view(), name='post_redirect'),

                # 硬编码URL重定向
                path('old-blog/', RedirectView.as_view(
                    url='/blog/',
                    permanent=True
                ), name='old_blog_redirect'),

                # 外部链接重定向
                path('go/', ExternalRedirectView.as_view(), name='external_redirect'),

                # 分类重定向
                path('category/<str:category_slug>/',
                     CategoryRedirectView.as_view(),
                     name='category_redirect'),
            ]
            ---

4.4 通用视图

01.列表和详情视图
    a.ListView
        a.功能说明
            ListView显示对象列表,通过model属性指定模型,paginate_by设置分页,ordering定义排序规则。
            通过get_queryset()自定义查询逻辑,get_context_data()添加额外上下文,支持搜索、过滤、分类等复杂列表展示需求。
        b.代码示例
            ---
            from django.views.generic import ListView
            from django.shortcuts import get_object_or_404
            from django.db.models import Q
            from .models import Post, Category
            from .forms import SearchForm

            class PostListView(ListView):
                """文章列表视图"""
                model = Post
                template_name = 'blog/post_list.html'
                context_object_name = 'posts'
                paginate_by = 10  # 每页10条记录
                ordering = '-created_at'  # 默认排序

                def get_queryset(self):
                    """自定义查询集"""
                    queryset = super().get_queryset().filter(published=True)

                    # 分类过滤
                    category_slug = self.kwargs.get('category_slug')
                    if category_slug:
                        category = get_object_or_404(Category, slug=category_slug)
                        queryset = queryset.filter(category=category)

                    # 搜索过滤
                    search_query = self.request.GET.get('q')
                    if search_query:
                        queryset = queryset.filter(
                            Q(title__icontains=search_query) |
                            Q(content__icontains=search_query) |
                            Q(author__username__icontains=search_query)
                        )

                    # 作者过滤
                    author_id = self.request.GET.get('author')
                    if author_id:
                        queryset = queryset.filter(author_id=author_id)

                    return queryset

                def get_context_data(self, **kwargs):
                    """添加额外的上下文数据"""
                    context = super().get_context_data(**kwargs)

                    # 添加分类列表
                    context['categories'] = Category.objects.all()

                    # 添加当前分类
                    category_slug = self.kwargs.get('category_slug')
                    if category_slug:
                        context['current_category'] = get_object_or_404(Category, slug=category_slug)

                    # 添加搜索表单
                    context['search_form'] = SearchForm(self.request.GET or None)

                    # 添加排序选项
                    context['sort_options'] = [
                        {'value': 'created_at', 'label': '创建时间'},
                        {'value': '-created_at', 'label': '最新创建'},
                        {'value': 'views_count', 'label': '浏览次数'},
                        {'value': '-views_count', 'label': '热门程度'},
                        {'value': 'title', 'label': '标题'},
                    ]

                    # 当前排序
                    context['current_sort'] = self.request.GET.get('sort', '-created_at')

                    return context

            class CategoryPostListView(ListView):
                """分类文章列表视图"""
                model = Post
                template_name = 'blog/category_posts.html'
                context_object_name = 'posts'
                paginate_by = 8

                def get_queryset(self):
                    """获取特定分类的文章"""
                    category = get_object_or_404(Category, slug=self.kwargs['slug'])
                    return Post.objects.filter(category=category, published=True)

                def get_context_data(self, **kwargs):
                    """添加分类信息到上下文"""
                    context = super().get_context_data(**kwargs)
                    category = get_object_or_404(Category, slug=self.kwargs['slug'])
                    context['category'] = category
                    context['page_title'] = f'{category.name} - 分类文章'
                    return context

            class AuthorPostListView(ListView):
                """作者文章列表视图"""
                model = Post
                template_name = 'blog/author_posts.html'
                context_object_name = 'posts'
                paginate_by = 6

                def get_queryset(self):
                    """获取特定作者的文章"""
                    author_id = self.kwargs.get('author_id')
                    return Post.objects.filter(author_id=author_id, published=True)

                def get_context_data(self, **kwargs):
                    """添加作者信息到上下文"""
                    context = super().get_context_data(**kwargs)
                    from django.contrib.auth.models import User
                    author = get_object_or_404(User, pk=self.kwargs['author_id'])
                    context['author'] = author
                    context['page_title'] = f'{author.username} - 文章列表'
                    return context
            ---
    b.DetailView
        a.功能说明
            DetailView显示单个对象详情,通过pk或slug参数查询对象,slug_url_kwarg和slug_field配置slug字段映射。
            通过get_object()自定义对象获取逻辑,get_context_data()添加相关数据如评论、相关文章等,适用于文章详情、用户资料等场景。
        b.代码示例
            ---
            from django.views.generic import DetailView
            from django.shortcuts import get_object_or_404
            from django.contrib.auth.models import User
            from django.utils import timezone
            from .models import Post, Comment
            from .forms import CommentForm

            class PostDetailView(DetailView):
                """文章详情视图"""
                model = Post
                template_name = 'blog/post_detail.html'
                context_object_name = 'post'
                slug_url_kwarg = 'slug'  # URL参数名称
                slug_field = 'slug'     # 模型字段名称
                pk_url_kwarg = 'pk'     # 主键URL参数名称

                def get_queryset(self):
                    """自定义查询集"""
                    return Post.objects.filter(published=True)

                def get_object(self, queryset=None):
                    """获取对象并增加浏览次数"""
                    if queryset is None:
                        queryset = self.get_queryset()

                    # 根据slug或pk获取对象
                    slug = self.kwargs.get(self.slug_url_kwarg)
                    pk = self.kwargs.get(self.pk_url_kwarg)

                    if slug:
                        obj = get_object_or_404(queryset, **{self.slug_field: slug})
                    else:
                        obj = get_object_or_404(queryset, pk=pk)

                    # 增加浏览次数
                    obj.views_count += 1
                    obj.save(update_fields=['views_count'])

                    return obj

                def get_context_data(self, **kwargs):
                    """添加额外的上下文数据"""
                    context = super().get_context_data(**kwargs)
                    post = context['post']

                    # 获取相关文章
                    context['related_posts'] = Post.objects.filter(
                        category=post.category
                    ).exclude(pk=post.pk).filter(published=True)[:3]

                    # 获取评论
                    context['comments'] = post.comments.filter(
                        approved=True
                    ).order_by('created_at')

                    # 添加评论表单
                    if self.request.user.is_authenticated:
                        context['comment_form'] = CommentForm()

                    # 获取作者其他文章
                    context['author_posts'] = Post.objects.filter(
                        author=post.author,
                        published=True
                    ).exclude(pk=post.pk)[:5]

                    return context

            class UserProfileView(DetailView):
                """用户资料视图"""
                model = User
                template_name = 'users/profile.html'
                context_object_name = 'user_profile'
                pk_url_kwarg = 'user_id'

                def get_context_data(self, **kwargs):
                    """添加用户相关数据到上下文"""
                    context = super().get_context_data(**kwargs)
                    user = context['user_profile']

                    # 用户统计信息
                    from django.db.models import Count, Sum
                    context['user_stats'] = {
                        'posts_count': Post.objects.filter(author=user, published=True).count(),
                        'total_views': Post.objects.filter(author=user, published=True).aggregate(
                            total=Sum('views_count')
                        )['total'] or 0,
                        'comments_count': Comment.objects.filter(user=user).count(),
                    }

                    # 最新文章
                    context['latest_posts'] = Post.objects.filter(
                        author=user,
                        published=True
                    ).order_by('-created_at')[:6]

                    return context

            class CategoryDetailView(DetailView):
                """分类详情视图"""
                model = Category
                template_name = 'blog/category_detail.html'
                context_object_name = 'category'
                slug_url_kwarg = 'slug'

                def get_context_data(self, **kwargs):
                    """添加分类相关数据到上下文"""
                    context = super().get_context_data(**kwargs)
                    category = context['category']

                    # 分页显示分类下的文章
                    from django.core.paginator import Paginator
                    posts = Post.objects.filter(
                        category=category,
                        published=True
                    ).order_by('-created_at')

                    paginator = Paginator(posts, 12)
                    page = self.request.GET.get('page', 1)
                    posts_page = paginator.get_page(page)

                    context['posts_page'] = posts_page
                    context['posts_count'] = posts.count()

                    return context
            ---
    c.列表详情混合使用
        a.功能说明
            通过判断URL参数动态选择显示列表或详情,使用SingleObjectMixin和ListView组合实现混合视图。
            混合视图减少代码重复,统一处理列表和详情的公共逻辑,适用于需要在同一页面切换列表和详情的场景。
        b.代码示例
            ---
            from django.views.generic import ListView, DetailView
            from django.views import View
            from django.shortcuts import render, get_object_or_404

            class PostListDetailView(View):
                """文章列表详情混合视图"""

                def get(self, request, slug=None, pk=None):
                    """根据参数决定显示列表还是详情"""
                    if slug or pk:
                        # 显示详情
                        return self.get_detail_view(request, slug, pk)
                    else:
                        # 显示列表
                        return self.get_list_view(request)

                def get_detail_view(self, request, slug, pk):
                    """获取详情视图内容"""
                    if slug:
                        post = get_object_or_404(Post, slug=slug, published=True)
                    else:
                        post = get_object_or_404(Post, pk=pk, published=True)

                    # 增加浏览次数
                    post.views_count += 1
                    post.save(update_fields=['views_count'])

                    # 获取相关数据
                    related_posts = Post.objects.filter(
                        category=post.category
                    ).exclude(pk=post.pk).filter(published=True)[:3]

                    comments = post.comments.filter(approved=True).order_by('created_at')

                    context = {
                        'post': post,
                        'related_posts': related_posts,
                        'comments': comments,
                        'view_mode': 'detail'
                    }

                    return render(request, 'blog/post_detail.html', context)

                def get_list_view(self, request):
                    """获取列表视图内容"""
                    posts = Post.objects.filter(published=True).order_by('-created_at')

                    # 处理搜索和过滤
                    search_query = request.GET.get('q')
                    category_id = request.GET.get('category')

                    if search_query:
                        posts = posts.filter(title__icontains=search_query)

                    if category_id:
                        posts = posts.filter(category_id=category_id)

                    # 分页处理
                    from django.core.paginator import Paginator
                    paginator = Paginator(posts, 10)
                    page = request.GET.get('page', 1)
                    posts_page = paginator.get_page(page)

                    context = {
                        'posts_page': posts_page,
                        'paginator': paginator,
                        'search_query': search_query or '',
                        'category_id': category_id or '',
                        'view_mode': 'list'
                    }

                    return render(request, 'blog/post_list.html', context)

            # 使用通用视图类实现类似功能
            from django.views.generic import ListView
            from django.views.generic.detail import SingleObjectMixin

            class PostMixinView(SingleObjectMixin, ListView):
                """文章混合视图"""
                model = Post
                template_name = 'blog/post_mixed.html'
                context_object_name = 'posts'
                paginate_by = 10

                def get(self, request, *args, **kwargs):
                    # 根据URL参数决定使用单个对象还是列表
                    if 'slug' in kwargs or 'pk' in kwargs:
                        self.object_list = None  # 不使用对象列表
                        self.object = self.get_object()
                        context = self.get_context_data(object=self.object)
                        return self.render_to_response(context)
                    else:
                        self.object = None
                        self.object_list = self.get_queryset()
                        context = self.get_context_data()
                        return self.render_to_response(context)

                def get_queryset(self):
                    """获取文章列表查询集"""
                    return Post.objects.filter(published=True)

                def get_object(self, queryset=None):
                    """获取单个文章对象"""
                    if queryset is None:
                        queryset = self.get_queryset()

                    slug = self.kwargs.get('slug')
                    pk = self.kwargs.get('pk')

                    if slug:
                        return get_object_or_404(queryset, slug=slug)
                    else:
                        return get_object_or_404(queryset, pk=pk)

                def get_context_data(self, **kwargs):
                    """构建上下文数据"""
                    context = super().get_context_data(**kwargs)

                    if hasattr(self, 'object') and self.object:
                        # 详情视图上下文
                        post = self.object
                        context['post'] = post
                        context['view_mode'] = 'detail'

                        # 相关文章
                        context['related_posts'] = Post.objects.filter(
                            category=post.category
                        ).exclude(pk=post.pk).filter(published=True)[:3]
                    else:
                        # 列表视图上下文
                        context['view_mode'] = 'list'

                    return context
            ---

4.5 视图高级功能

01.Mixin模式
    a.常用Mixin类
        a.功能说明
            Django内置Mixin类提供可复用功能,LoginRequiredMixin要求用户登录,PermissionRequiredMixin检查特定权限,UserPassesTestMixin执行自定义测试。
            Mixin通过多重继承组合功能,遵循单一职责原则,提高代码复用性和可维护性。
        b.代码示例
            ---
            from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin, UserPassesTestMixin
            from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
            from django.urls import reverse_lazy
            from django.http import HttpResponseForbidden
            from .models import Post

            class PostCreateView(LoginRequiredMixin, CreateView):
                """创建文章视图(需要登录)"""
                model = Post
                template_name = 'blog/post_form.html'
                fields = ['title', 'content', 'category', 'tags', 'featured_image']
                success_url = reverse_lazy('post_list')

                def form_valid(self, form):
                    """设置作者为当前用户"""
                    form.instance.author = self.request.user
                    return super().form_valid(form)

            class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
                """更新文章视图(需要登录且为作者)"""
                model = Post
                template_name = 'blog/post_form.html'
                fields = ['title', 'content', 'category', 'tags', 'featured_image']

                def test_func(self):
                    """测试用户是否为文章作者"""
                    post = self.get_object()
                    return post.author == self.request.user

                def handle_no_permission(self):
                    """处理权限不足的情况"""
                    return HttpResponseForbidden("您没有权限编辑此文章")

                def get_success_url(self):
                    """更新成功后重定向到文章详情页"""
                    return reverse_lazy('post_detail', kwargs={'pk': self.object.pk})

            class PostDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
                """删除文章视图(需要登录和特定权限)"""
                model = Post
                template_name = 'blog/post_confirm_delete.html'
                success_url = reverse_lazy('post_list')
                permission_required = 'blog.delete_post'

                def get_queryset(self):
                    """只能删除自己创建的文章"""
                    return Post.objects.filter(author=self.request.user)

            # 自定义Mixin类
            class AuthorRequiredMixin:
                """要求用户为作者的Mixin"""
                def dispatch(self, request, *args, **kwargs):
                    if not request.user.is_authenticated:
                        return self.handle_no_permission()

                    if not hasattr(request.user, 'author_profile'):
                        return HttpResponseForbidden("您没有作者权限")

                    return super().dispatch(request, *args, **kwargs)

            class PublishedRequiredMixin:
                """文章必须已发布的Mixin"""
                def get_queryset(self):
                    return super().get_queryset().filter(published=True)

            class SEOContextMixin:
                """SEO上下文Mixin"""
                def get_context_data(self, **kwargs):
                    context = super().get_context_data(**kwargs)

                    # 添加默认SEO信息
                    context.update({
                        'meta_title': getattr(self, 'meta_title', '默认标题'),
                        'meta_description': getattr(self, 'meta_description', '默认描述'),
                        'meta_keywords': getattr(self, 'meta_keywords', ''),
                    })

                    # 如果是详情视图,添加对象的SEO信息
                    if hasattr(self, 'object') and self.object:
                        if hasattr(self.object, 'title'):
                            context['meta_title'] = self.object.title
                        if hasattr(self.object, 'content'):
                            content = self.object.content[:150]
                            context['meta_description'] = content.replace('\n', ' ').strip()

                    return context

            class CacheMixin:
                """缓存Mixin"""
                cache_timeout = 300  # 5分钟

                def dispatch(self, request, *args, **kwargs):
                    # 如果是GET请求且用户未登录,使用缓存
                    if request.method == 'GET' and not request.user.is_authenticated:
                        cache_key = self.get_cache_key(request)
                        cached_response = cache.get(cache_key)

                        if cached_response:
                            return cached_response

                    response = super().dispatch(request, *args, **kwargs)

                    if request.method == 'GET' and not request.user.is_authenticated:
                        cache.set(cache_key, response, self.cache_timeout)

                    return response

                def get_cache_key(self, request):
                    """生成缓存键"""
                    return f"{self.__class__.__name__}:{request.path}:{request.GET.urlencode()}"

            # 使用自定义Mixin
            class PostDetailView(SEOContextMixin, PublishedRequiredMixin, DetailView):
                """文章详情视图"""
                model = Post
                template_name = 'blog/post_detail.html'
                context_object_name = 'post'
                slug_url_kwarg = 'slug'

                meta_title = '博客文章详情'
                meta_description = '查看精彩的博客文章内容'

            class PublicPostListView(CacheMixin, SEOContextMixin, ListView):
                """公开文章列表视图(带缓存)"""
                model = Post
                template_name = 'blog/post_list.html'
                context_object_name = 'posts'
                paginate_by = 10
                cache_timeout = 600  # 10分钟

                meta_title = '博客文章列表'
                meta_description = '浏览最新的博客文章'
            ---
    b.Mixin组合策略
        a.功能说明
            多重继承遵循MRO(方法解析顺序)从左到右查找方法,Mixin调用顺序影响功能执行流程,需避免方法名冲突和循环依赖。
            合理组合Mixin需考虑功能优先级、依赖关系和super()调用链,确保各Mixin协同工作。
        b.代码示例
            ---
            from django.views.generic import View
            from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin

            class PostCRUDMixin:
                """文章CRUD操作Mixin"""

                def get_success_url(self):
                    """根据操作类型返回不同的URL"""
                    if self.request.POST.get('_save_and_continue'):
                        return reverse_lazy('post_edit', kwargs={'pk': self.object.pk})
                    elif self.request.POST.get('_save_and_new'):
                        return reverse_lazy('post_create')
                    else:
                        return reverse_lazy('post_list')

            class PostFormValidationMixin:
                """文章表单验证Mixin"""

                def form_valid(self, form):
                    """表单验证成功时的处理"""
                    response = super().form_valid(form)

                    # 发送通知
                    if self.object.published:
                        send_post_published_notification(self.object)

                    # 记录日志
                    log_user_action(self.request.user, f'创建/更新文章: {self.object.title}')

                    return response

            class PostContextMixin:
                """文章上下文Mixin"""

                def get_context_data(self, **kwargs):
                    context = super().get_context_data(**kwargs)

                    # 添加常用上下文
                    context['categories'] = Category.objects.all()
                    context['tags'] = Tag.objects.all()

                    # 如果是编辑视图,添加额外信息
                    if hasattr(self, 'object') and self.object:
                        context['edit_mode'] = True
                        context['post_versions'] = PostVersion.objects.filter(
                            post=self.object
                        ).order_by('-created_at')[:5]

                    return context

            # 组合使用多个Mixin
            class PostCreateView(
                LoginRequiredMixin,
                PermissionRequiredMixin,
                PostCRUDMixin,
                PostFormValidationMixin,
                PostContextMixin,
                CreateView
            ):
                """创建文章视图(组合多个Mixin)"""
                model = Post
                template_name = 'blog/post_form.html'
                form_class = PostForm
                permission_required = 'blog.add_post'

                def form_invalid(self, form):
                    """表单验证失败时的处理"""
                    messages.error(self.request, '表单验证失败,请检查输入内容')
                    return super().form_invalid(form)

            class PostUpdateView(
                LoginRequiredMixin,
                UserPassesTestMixin,
                PostCRUDMixin,
                PostFormValidationMixin,
                PostContextMixin,
                UpdateView
            ):
                """更新文章视图"""
                model = Post
                template_name = 'blog/post_form.html'
                form_class = PostForm

                def test_func(self):
                    """测试用户权限"""
                    return self.get_object().author == self.request.user

            # 动态Mixin组合
            def get_view_class_with_mixins(base_view, *mixins):
                """动态组合Mixin"""
                class DynamicView(*mixins, base_view):
                    pass

                return DynamicView

            # 根据条件动态选择Mixin
            def create_post_view(request):
                """根据用户权限创建不同的视图"""
                if request.user.has_perm('blog.moderate_post'):
                    # 管理员视图,使用所有功能
                    return AdminPostView.as_view()
                elif request.user.has_perm('blog.create_post'):
                    # 普通作者视图
                    return AuthorPostView.as_view()
                else:
                    # 游客只读视图
                    return PublicPostView.as_view()

            # 动态Mixin选择
            class DynamicMixinView:
                """根据请求动态选择Mixin"""
                def get_mixin_classes(self):
                    """获取应该使用的Mixin类"""
                    mixins = []

                    if self.request.user.is_authenticated:
                        mixins.append(LoginRequiredMixin)

                    if self.request.user.is_staff:
                        mixins.append(StaffRequiredMixin)

                    if self.request.GET.get('preview'):
                        mixins.append(PreviewModeMixin)

                    return mixins

                def get_view_class(self):
                    """获取最终的视图类"""
                    base_view = self.base_view_class
                    mixins = self.get_mixin_classes()

                    return type(
                        f'Dynamic{base_view.__name__}',
                        tuple(mixins) + (base_view,),
                        {}
                    )

            # 策略模式使用Mixin
            class ViewStrategyMixin:
                """视图策略Mixin"""
                strategy_map = {}

                def get_strategy(self):
                    """获取当前策略"""
                    strategy_key = self.get_strategy_key()
                    return self.strategy_map.get(strategy_key)

                def get_strategy_key(self):
                    """获取策略键"""
                    return self.request.GET.get('strategy', 'default')

                def dispatch(self, request, *args, **kwargs):
                    strategy = self.get_strategy()
                    if strategy:
                        # 应用策略特定的Mixin
                        return strategy.apply(self)

                    return super().dispatch(request, *args, **kwargs)
            ---
    c.自定义Mixin开发
        a.功能说明
            自定义Mixin封装可复用的视图逻辑,通过类属性提供配置选项,使用super()保持方法调用链完整。
            Mixin应专注单一功能,提供清晰的接口和文档,支持灵活配置和扩展,便于在多个视图中复用。
        b.代码示例
            ---
            from django.core.exceptions import ImproperlyConfigured
            from django.contrib import messages
            from django.utils.timezone import now
            import json

            class AjaxResponseMixin:
                """AJAX响应Mixin"""

                ajax_template_name = None
                ajax_context_name = 'data'

                def get_ajax_template_names(self):
                    """获取AJAX模板名称"""
                    if self.ajax_template_name:
                        return [self.ajax_template_name]

                    # 默认使用原模板名加上_ajax后缀
                    template_name = self.get_template_names()[0]
                    name, ext = template_name.rsplit('.', 1)
                    return [f"{name}_ajax.{ext}"]

                def render_to_response(self, context, **response_kwargs):
                    """根据请求类型返回不同响应"""
                    if self.request.headers.get('x-requested-with') == 'XMLHttpRequest':
                        # AJAX请求,返回JSON或部分模板
                        if self.request.GET.get('format') == 'json':
                            data = context.get(self.ajax_context_name, context)
                            return JsonResponse(data)
                        else:
                            template_names = self.get_ajax_template_names()
                            return self.response_class(
                                request=self.request,
                                template=template_names,
                                context=context,
                                **response_kwargs
                            )
                    else:
                        # 普通请求,返回完整模板
                        return super().render_to_response(context, **response_kwargs)

            class TimestampMixin:
                """时间戳Mixin"""
                add_created_at = True
                add_updated_at = True
                timestamp_field = 'created_at'
                update_timestamp_field = 'updated_at'

                def form_valid(self, form):
                    """表单验证时添加时间戳"""
                    response = super().form_valid(form)

                    if self.add_created_at and hasattr(form.instance, 'created_at'):
                        form.instance.created_at = now()

                    if self.add_updated_at and hasattr(form.instance, 'updated_at'):
                        form.instance.updated_at = now()

                    return response

            class BulkOperationMixin:
                """批量操作Mixin"""
                bulk_action_param = 'bulk_action'
                bulk_ids_param = 'bulk_ids'

                def post(self, request, *args, **kwargs):
                    """处理POST请求,检查批量操作"""
                    bulk_action = request.POST.get(self.bulk_action_param)
                    bulk_ids = request.POST.getlist(self.bulk_ids_param)

                    if bulk_action and bulk_ids:
                        return self.handle_bulk_operation(bulk_action, bulk_ids)

                    return super().post(request, *args, **kwargs)

                def handle_bulk_operation(self, action, ids):
                    """处理批量操作"""
                    try:
                        queryset = self.get_queryset().filter(pk__in=ids)
                        count = queryset.count()

                        if action == 'publish':
                            queryset.update(published=True)
                            messages.success(
                                self.request, f'成功发布 {count} 篇文章'
                            )
                        elif action == 'unpublish':
                            queryset.update(published=False)
                            messages.success(
                                self.request, f'成功取消发布 {count} 篇文章'
                            )
                        elif action == 'delete':
                            queryset.delete()
                            messages.success(
                                self.request, f'成功删除 {count} 篇文章'
                            )
                        else:
                            messages.error(
                                self.request, '未知的批量操作'
                            )

                    except Exception as e:
                        messages.error(
                            self.request, f'批量操作失败: {str(e)}'
                        )

                    return self.get(self.request)

            class FilterMixin:
                """过滤器Mixin"""
                filter_fields = {}
                filter_class = None

                def get_queryset(self):
                    """应用过滤器到查询集"""
                    queryset = super().get_queryset()

                    if self.filter_class:
                        # 使用过滤器类
                        filter_obj = self.filter_class(
                            self.request.GET, queryset=queryset
                        )
                        return filter_obj.qs

                    # 使用字段过滤
                    for field_name, filter_value in self.filter_fields.items():
                        request_value = self.request.GET.get(field_name)
                        if request_value:
                            filter_kwargs = {filter_value: request_value}
                            queryset = queryset.filter(**filter_kwargs)

                    return queryset

                def get_context_data(self, **kwargs):
                    """添加过滤上下文"""
                    context = super().get_context_data(**kwargs)

                    # 添加当前过滤参数
                    context['current_filters'] = {
                        field: self.request.GET.get(field)
                        for field in self.filter_fields.keys()
                    }

                    return context

            # 配置化Mixin
            class ConfigurableMixin:
                """可配置Mixin"""
                config_options = {}

                def __init__(self, *args, **kwargs):
                    super().__init__(*args, **kwargs)
                    self._config_cache = {}

                def get_config(self, key, default=None):
                    """获取配置值"""
                    if key not in self._config_cache:
                        self._config_cache[key] = self._resolve_config(key, default)

                    return self._config_cache[key]

                def _resolve_config(self, key, default):
                    """解析配置值"""
                    # 1. 检查类属性
                    if hasattr(self, key):
                        return getattr(self, key)

                    # 2. 检查配置选项
                    if key in self.config_options:
                        return self.config_options[key]

                    # 3. 检查设置
                    from django.conf import settings
                    settings_key = f"{self.__class__.__name__.upper()}_{key.upper()}"
                    return getattr(settings, settings_key, default)

            # 性能监控Mixin
            class PerformanceMonitorMixin:
                """性能监控Mixin"""
                monitor_enabled = True
                slow_query_threshold = 1000  # 毫秒

                def dispatch(self, request, *args, **kwargs):
                    if not self.monitor_enabled:
                        return super().dispatch(request, *args, **kwargs)

                    import time
                    start_time = time.time()

                    try:
                        response = super().dispatch(request, *args, **kwargs)

                        # 记录性能数据
                        end_time = time.time()
                        duration = (end_time - start_time) * 1000  # 转换为毫秒

                        if duration > self.slow_query_threshold:
                            # 记录慢请求
                            logger.warning(
                                f"Slow request: {request.path} took {duration:.2f}ms"
                            )

                        # 添加性能头信息
                        response['X-Response-Time'] = f"{duration:.2f}ms"

                        return response

                    except Exception as e:
                        # 记录错误信息
                        logger.error(
                            f"Error in {self.__class__.__name__}: {str(e)}"
                        )
                        raise
            ---

02.视图装饰器与类视图集成
    a.method_decorator
        a.功能说明
            method_decorator将函数装饰器应用到类视图方法,通过name参数指定装饰目标方法(如'dispatch'、'get'、'post')。
            支持装饰单个方法或使用列表装饰多个方法,保持函数装饰器在类视图中的可用性。
        b.代码示例
            ---
            from django.utils.decorators import method_decorator
            from django.contrib.auth.decorators import login_required, user_passes_test
            from django.views.decorators.cache import cache_page, never_cache
            from django.views.decorators.csrf import csrf_exempt
            from django.views.decorators.http import require_POST
            from django.views.generic import View, ListView, CreateView

            # 装饰单个方法
            class PostView(View):
                def get(self, request):
                    return render(request, 'post_list.html')

                @method_decorator(require_POST)
                @method_decorator(csrf_exempt)
                def post(self, request):
                    # POST方法免CSRF保护
                    return JsonResponse({'status': 'success'})

                @method_decorator(login_required)
                def put(self, request):
                    # PUT方法需要登录
                    return JsonResponse({'status': 'updated'})

            # 装饰所有方法
            @method_decorator([login_required, cache_page(60 * 15)], name='dispatch')
            class ProtectedPostView(ListView):
                """需要登录和缓存的视图"""
                model = Post
                template_name = 'blog/protected_post_list.html'
                context_object_name = 'posts'

                def get_queryset(self):
                    return Post.objects.filter(author=self.request.user)

            # 装饰特定方法
            @method_decorator(login_required, name='get')
            @method_decorator(user_passes_test(lambda u: u.is_staff), name='post')
            class AdminOnlyView(View):
                """不同方法需要不同权限"""
                def get(self, request):
                    # GET请求需要登录
                    return render(request, 'admin/dashboard.html')

                def post(self, request):
                    # POST请求需要管理员权限
                    if request.is_ajax():
                        return JsonResponse({'status': 'ok'})
                    return redirect('admin:dashboard')

            # 组合使用method_decorator和Mixin
            class CustomLoginRequiredMixin:
                """自定义登录Mixin,使用method_decorator"""

                @method_decorator(login_required)
                def dispatch(self, request, *args, **kwargs):
                    return super().dispatch(request, *args, **kwargs)

            class PostCreateView(CustomLoginRequiredMixin, CreateView):
                """使用自定义登录Mixin"""
                model = Post
                fields = ['title', 'content']
                template_name = 'blog/post_form.html'

                def form_valid(self, form):
                    form.instance.author = self.request.user
                    return super().form_valid(form)

            # 动态装饰器应用
            class DynamicDecoratorsView(View):
                """动态应用装饰器"""
                decorators_map = {
                    'GET': [login_required],
                    'POST': [login_required, require_POST],
                    'PUT': [login_required, csrf_exempt],
                }

                def dispatch(self, request, *args, **kwargs):
                    # 获取当前HTTP方法的装饰器
                    decorators = self.decorators_map.get(request.method.upper(), [])

                    # 应用装饰器
                    view_func = super().dispatch
                    for decorator in decorators:
                        view_func = decorator(view_func)

                    return view_func(request, *args, **kwargs)

            # 装饰器工厂
            def class_view_decorator(decorator):
                """类视图装饰器工厂"""
                def _decorator(cls):
                    cls.dispatch = method_decorator(decorator)(cls.dispatch)
                    return cls
                return _decorator

            # 使用装饰器工厂
            @class_view_decorator(login_required)
            class LoginRequiredPostListView(ListView):
                """需要登录的文章列表视图"""
                model = Post
                template_name = 'blog/post_list.html'

            @class_view_decorator(cache_page(60 * 30))
            class CachedPostListView(ListView):
                """缓存的文章列表视图"""
                model = Post
                template_name = 'blog/post_list.html'

            # 组合多个装饰器
            def require_authenticated(view_func):
                """要求用户认证的装饰器"""
                def wrapper(request, *args, **kwargs):
                    if not request.user.is_authenticated:
                        return JsonResponse({
                            'error': 'Authentication required',
                            'login_url': '/login/'
                        }, status=401)
                    return view_func(request, *args, **kwargs)
                return wrapper

            def rate_limit(max_requests=100, window=3600):
                """速率限制装饰器"""
                def decorator(view_func):
                    def wrapper(request, *args, **kwargs):
                        # 实现速率限制逻辑
                        client_ip = request.META.get('REMOTE_ADDR')
                        cache_key = f"rate_limit_{client_ip}"
                        request_count = cache.get(cache_key, 0)

                        if request_count >= max_requests:
                            return JsonResponse({
                                'error': 'Rate limit exceeded'
                            }, status=429)

                        cache.set(cache_key, request_count + 1, window)
                        return view_func(request, *args, **kwargs)
                    return wrapper
                return decorator

            @class_view_decorator(require_authenticated)
            @class_view_decorator(rate_limit(max_requests=1000, window=3600))
            class APILoginRequiredView(View):
                """API视图,需要认证和速率限制"""
                def get(self, request):
                    return JsonResponse({
                        'user': request.user.username,
                        'message': 'Authenticated API access'
                    })
            ---
    b.装饰器链和优先级
        a.功能说明
            多个装饰器按从下到上的顺序执行(装饰时从上到下,执行时从下到上),装饰器链的作用域影响功能范围。
            条件装饰器根据配置或运行时状态决定是否应用,提供灵活的装饰器组合策略。
        b.代码示例
            ---
            from functools import wraps
            from django.utils.decorators import available_attrs

            def conditional_decorator(condition, decorator):
                """条件装饰器"""
                def _decorator(view_func):
                    if condition:
                        return decorator(view_func)
                    return view_func
                return _decorator

            def debug_only(decorator):
                """仅在DEBUG模式下生效的装饰器"""
                from django.conf import settings
                return conditional_decorator(settings.DEBUG, decorator)

            def staff_only(decorator):
                """仅对管理员生效的装饰器"""
                def _decorator(view_func):
                    @wraps(view_func, assigned=available_attrs(view_func))
                    def wrapper(request, *args, **kwargs):
                        if request.user.is_staff:
                            return decorator(view_func)(request, *args, **kwargs)
                        return view_func(request, *args, **kwargs)
                    return wrapper
                return _decorator

            # 使用条件装饰器
            @method_decorator(debug_only(cache_page(60 * 5)), name='dispatch')
            class DebugCacheView(ListView):
                """仅在调试模式下缓存的视图"""
                model = Post
                template_name = 'blog/debug_cache.html'

            @method_decorator(staff_only(login_required), name='dispatch')
            class StaffOrLoginView(View):
                """管理员使用登录装饰器,普通用户直接访问"""
                def get(self, request):
                    if request.user.is_staff:
                        # 管理员可以看到额外信息
                        context = {
                            'is_staff': True,
                            'debug_info': get_debug_info()
                        }
                    else:
                        context = {'is_staff': False}

                    return render(request, 'conditional_view.html', context)

            # 装饰器优先级示例
            def log_execution(decorator):
                """记录执行日志装饰器"""
                def _decorator(view_func):
                    @wraps(view_func, assigned=available_attrs(view_func))
                    def wrapper(*args, **kwargs):
                        logger.info(f"Starting {view_func.__name__}")
                        try:
                            result = view_func(*args, **kwargs)
                            logger.info(f"Completed {view_func.__name__}")
                            return result
                        except Exception as e:
                            logger.error(f"Error in {view_func.__name__}: {str(e)}")
                            raise
                    return wrapper
                return _decorator

            def measure_time(decorator):
                """测量执行时间装饰器"""
                def _decorator(view_func):
                    @wraps(view_func, assigned=available_attrs(view_func))
                    def wrapper(*args, **kwargs):
                        import time
                        start_time = time.time()
                        result = view_func(*args, **kwargs)
                        end_time = time.time()
                        duration = end_time - start_time
                        logger.info(f"{view_func.__name__} took {duration:.3f}s")
                        return result
                    return wrapper
                return _decorator

            # 装饰器链:先执行时间测量,再执行日志记录
            @method_decorator(measure_time)
            @method_decorator(log_execution)
            @method_decorator(login_required)
            class MonitoredPostListView(ListView):
                """带监控的视图"""
                model = Post
                template_name = 'blog/monitored_list.html'

            # 自定义装饰器类
            class DecoratorChain:
                """装饰器链类"""
                def __init__(self, *decorators):
                    self.decorators = decorators

                def apply(self, view_func):
                    """应用装饰器链"""
                    result = view_func
                    for decorator in self.decorators:
                        result = decorator(result)
                    return result

            # 使用装饰器链
            decorators = DecoratorChain(
                login_required,
                cache_page(60 * 15),
                measure_time,
                log_execution
            )

            @method_decorator(decorators.apply, name='dispatch')
            class ChainedDecoratorsView(ListView):
                """使用装饰器链的视图"""
                model = Post
                template_name = 'blog/chained_decorators.html'
            ---
    c.自定义装饰器开发
        a.功能说明
            自定义装饰器封装特定的视图处理逻辑,支持参数配置和条件判断,使用wraps保持被装饰函数的元数据。
            装饰器可实现API验证、限流、功能开关、权限检查等横切关注点,提高代码的模块化和可维护性。
        b.代码示例
            ---
            from django.utils.decorators import method_decorator, available_attrs
            from django.http import JsonResponse, HttpResponseForbidden
            from django.core.exceptions import PermissionDenied
            from functools import wraps
            import json
            import time

            def api_view(http_methods=None):
                """API视图装饰器"""
                if http_methods is None:
                    http_methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']

                def decorator(view_func):
                    @wraps(view_func, assigned=available_attrs(view_func))
                    def wrapper(request, *args, **kwargs):
                        # 检查HTTP方法
                        if request.method not in http_methods:
                            return JsonResponse({
                                'error': f'Method {request.method} not allowed'
                            }, status=405)

                        # 检查Content-Type
                        if request.method in ['POST', 'PUT', 'PATCH']:
                            content_type = request.content_type
                            if not content_type or 'application/json' not in content_type:
                                return JsonResponse({
                                    'error': 'Content-Type must be application/json'
                                }, status=400)

                        # 执行视图
                        try:
                            result = view_func(request, *args, **kwargs)

                            # 如果返回的是字典,转换为JSON响应
                            if isinstance(result, dict):
                                return JsonResponse(result)

                            return result

                        except Exception as e:
                            return JsonResponse({
                                'error': str(e),
                                'type': type(e).__name__
                            }, status=500)

                    return wrapper
                return decorator

            def throttle(rate='100/h'):
                """API限流装饰器"""
                def decorator(view_func):
                    @wraps(view_func, assigned=available_attrs(view_func))
                    def wrapper(request, *args, **kwargs):
                        # 解析速率限制
                        if '/' in rate:
                            requests, period = rate.split('/')
                            requests = int(requests)

                            if period == 's':
                                period_seconds = 1
                            elif period == 'm':
                                period_seconds = 60
                            elif period == 'h':
                                period_seconds = 3600
                            elif period == 'd':
                                period_seconds = 86400
                            else:
                                period_seconds = 3600
                        else:
                            requests = int(rate)
                            period_seconds = 3600

                        # 获取客户端标识
                        client_id = (
                            request.META.get('HTTP_X_FORWARDED_FOR') or
                            request.META.get('REMOTE_ADDR') or
                            request.session.session_key or
                            'anonymous'
                        )

                        # 检查限流
                        from django.core.cache import cache
                        cache_key = f"throttle_{client_id}:{view_func.__name__}"
                        current_requests = cache.get(cache_key, [])

                        # 清理过期的请求记录
                        now = time.time()
                        current_requests = [
                            req_time for req_time in current_requests
                            if now - req_time < period_seconds
                        ]

                        if len(current_requests) >= requests:
                            return JsonResponse({
                                'error': 'Rate limit exceeded',
                                'retry_after': period_seconds
                            }, status=429)

                        # 记录当前请求
                        current_requests.append(now)
                        cache.set(cache_key, current_requests, period_seconds)

                        return view_func(request, *args, **kwargs)

                    return wrapper
                return decorator

            def feature_flag(feature_name, default=False):
                """功能开关装饰器"""
                def decorator(view_func):
                    @wraps(view_func, assigned=available_attrs(view_func))
                    def wrapper(request, *args, **kwargs):
                        from django.conf import settings

                        # 检查功能开关
                        feature_flags = getattr(settings, 'FEATURE_FLAGS', {})
                        is_enabled = feature_flags.get(feature_name, default)

                        if not is_enabled:
                            return JsonResponse({
                                'error': f'Feature {feature_name} is disabled'
                            }, status=403)

                        return view_func(request, *args, **kwargs)

                    return wrapper
                return decorator

            def permission_check(permissions, any_user=False):
                """权限检查装饰器"""
                def decorator(view_func):
                    @wraps(view_func, assigned=available_attrs(view_func))
                    def wrapper(request, *args, **kwargs):
                        if not request.user.is_authenticated:
                            return JsonResponse({
                                'error': 'Authentication required'
                            }, status=401)

                        if any_user:
                            # any_user=True时,已认证用户即可访问
                            return view_func(request, *args, **kwargs)

                        # 检查具体权限
                        if isinstance(permissions, str):
                            permissions = [permissions]

                        has_permission = any(
                            request.user.has_perm(perm) for perm in permissions
                        )

                        if not has_permission:
                            return JsonResponse({
                                'error': 'Permission denied',
                                'required_permissions': permissions
                            }, status=403)

                        return view_func(request, *args, **kwargs)

                    return wrapper
                return decorator

            def validate_schema(schema=None):
                """请求schema验证装饰器"""
                def decorator(view_func):
                    @wraps(view_func, assigned=available_attrs(view_func))
                    def wrapper(request, *args, **kwargs):
                        if request.method in ['POST', 'PUT', 'PATCH'] and schema:
                            try:
                                # 解析JSON数据
                                data = json.loads(request.body)

                                # 验证schema
                                from jsonschema import validate, ValidationError
                                validate(data, schema)

                            except json.JSONDecodeError:
                                return JsonResponse({
                                    'error': 'Invalid JSON format'
                                }, status=400)

                            except ValidationError as e:
                                return JsonResponse({
                                    'error': 'Validation error',
                                    'details': e.message
                                }, status=400)

                            # 将验证后的数据添加到请求中
                            request.validated_data = data

                        return view_func(request, *args, **kwargs)

                    return wrapper
                return decorator

            # 使用自定义装饰器
            @method_decorator(api_view(['GET', 'POST']), name='dispatch')
            @method_decorator(throttle('10/m'), name='dispatch')
            @method_decorator(feature_flag('new_api'), name='dispatch')
            class NewAPIPostView(View):
                """新API文章视图"""
                def get(self, request):
                    posts = Post.objects.filter(published=True)
                    data = {
                        'posts': [
                            {
                                'id': post.id,
                                'title': post.title,
                                'author': post.author.username,
                                'created_at': post.created_at.isoformat()
                            }
                            for post in posts
                        ]
                    }
                    return data

                @method_decorator(validate_schema({
                    'type': 'object',
                    'properties': {
                        'title': {'type': 'string', 'minLength': 1},
                        'content': {'type': 'string', 'minLength': 1},
                        'category': {'type': 'string'}
                    },
                    'required': ['title', 'content']
                }))
                def post(self, request):
                    data = request.validated_data
                    post = Post.objects.create(
                        title=data['title'],
                        content=data['content'],
                        author=request.user,
                        category_id=data.get('category')
                    )

                    return {
                        'id': post.id,
                        'message': 'Post created successfully'
                    }

            # 装饰器工厂模式
            class DecoratorFactory:
                """装饰器工厂类"""
                @staticmethod
                def create_auth_decorator(permissions=None, roles=None):
                    """创建认证装饰器"""
                    def decorator(view_func):
                        @wraps(view_func, assigned=available_attrs(view_func))
                        def wrapper(request, *args, **kwargs):
                            if not request.user.is_authenticated:
                                raise PermissionDenied

                            if permissions:
                                if isinstance(permissions, str):
                                    permissions = [permissions]

                                if not any(request.user.has_perm(p) for p in permissions):
                                    raise PermissionDenied

                            if roles:
                                if isinstance(roles, str):
                                    roles = [roles]

                                user_roles = request.user.groups.values_list('name', flat=True)
                                if not any(role in user_roles for role in roles):
                                    raise PermissionDenied

                            return view_func(request, *args, **kwargs)

                        return wrapper
                    return decorator

                @staticmethod
                def create_cache_decorator(timeout=300, key_func=None):
                    """创建缓存装饰器"""
                    def decorator(view_func):
                        @wraps(view_func, assigned=available_attrs(view_func))
                        def wrapper(request, *args, **kwargs):
                            from django.core.cache import cache

                            if key_func:
                                cache_key = key_func(request, view_func, *args, **kwargs)
                            else:
                                cache_key = f"{view_func.__name__}:{request.path}:{request.GET.urlencode()}"

                            result = cache.get(cache_key)
                            if result is not None:
                                return result

                            result = view_func(request, *args, **kwargs)
                            cache.set(cache_key, result, timeout)

                            return result

                        return wrapper
                    return decorator

            # 使用装饰器工厂
            @method_decorator(
                DecoratorFactory.create_auth_decorator(
                    permissions=['blog.add_post'],
                    roles=['author']
                ),
                name='dispatch'
            )
            class AuthorOnlyPostView(View):
                """仅作者可访问的文章视图"""
                def post(self, request):
                    # 只有拥有blog.add_post权限或author角色的用户才能访问
                    return JsonResponse({'message': 'Access granted'})
            ---

4.6 视图性能优化

01.查询优化
    a.select_related和prefetch_related
        a.功能说明
            select_related通过JOIN减少外键和一对一关系的查询次数,prefetch_related通过额外查询优化多对多和反向关系。
            使用Prefetch对象自定义预加载查询集,annotate添加聚合字段避免后续查询,有效解决N+1查询问题。
        b.代码示例
            ---
            from django.views.generic import ListView, DetailView
            from django.db.models import Prefetch, Count, Q
            from .models import Post, Category, Comment, Tag

            class OptimizedPostListView(ListView):
                """优化的文章列表视图"""
                model = Post
                template_name = 'blog/post_list.html'
                context_object_name = 'posts'
                paginate_by = 20

                def get_queryset(self):
                    """优化查询集,减少数据库访问"""
                    queryset = Post.objects.filter(published=True)

                    # 使用select_related优化外键关系
                    queryset = queryset.select_related(
                        'author',  # 一对多关系
                        'category'  # 一对多关系
                    )

                    # 使用prefetch_related优化多对多和反向关系
                    queryset = queryset.prefetch_related(
                        'tags',  # 多对多关系
                        Prefetch(
                            'comments',
                            queryset=Comment.objects.filter(approved=True),
                            to_attr='approved_comments'
                        )
                    )

                    # 添加注解字段,避免后续查询
                    queryset = queryset.annotate(
                        comments_count=Count('comments', filter=Q(comments__approved=True)),
                        likes_count=Count('likes')
                    )

                    return queryset.order_by('-created_at')

                def get_context_data(self, **kwargs):
                    context = super().get_context_data(**kwargs)

                    # 优化分类查询,避免重复查询
                    context['categories'] = Category.objects.prefetch_related(
                        Prefetch(
                            'post_set',
                            queryset=Post.objects.filter(published=True),
                            to_attr='published_posts'
                        )
                    ).annotate(posts_count=Count('post', filter=Q(post__published=True)))

                    return context

            class OptimizedPostDetailView(DetailView):
                """优化的文章详情视图"""
                model = Post
                template_name = 'blog/post_detail.html'
                context_object_name = 'post'

                def get_queryset(self):
                    """优化查询集"""
                    return Post.objects.select_related(
                        'author',
                        'category'
                    ).prefetch_related(
                        'tags',
                        Prefetch(
                            'comments',
                            queryset=Comment.objects.filter(
                                approved=True
                            ).select_related('user'),
                            to_attr='approved_comments'
                        ),
                        'likes'
                    ).filter(published=True)

                def get_context_data(self, **kwargs):
                    context = super().get_context_data(**kwargs)
                    post = context['post']

                    # 相关文章(避免N+1查询)
                    context['related_posts'] = Post.objects.filter(
                        category=post.category,
                        published=True
                    ).exclude(pk=post.pk).select_related(
                        'author'
                    )[:6]

                    # 作者的其他文章
                    context['author_posts'] = Post.objects.filter(
                        author=post.author,
                        published=True
                    ).exclude(pk=post.pk).select_related(
                        'category'
                    )[:5]

                    return context

            class OptimizedCategoryDetailView(DetailView):
                """优化的分类详情视图"""
                model = Category
                template_name = 'blog/category_detail.html'
                context_object_name = 'category'

                def get_queryset(self):
                    return Category.objects.prefetch_related(
                        Prefetch(
                            'post_set',
                            queryset=Post.objects.filter(
                                published=True
                            ).select_related('author', 'category').prefetch_related('tags'),
                            to_attr='published_posts'
                        )
                    )

                def get_context_data(self, **kwargs):
                    context = super().get_context_data(**kwargs)
                    category = context['category']

                    # 直接使用预加载的文章,无需额外查询
                    published_posts = category.published_posts

                    # 分页处理
                    from django.core.paginator import Paginator
                    paginator = Paginator(published_posts, 12)
                    page = self.request.GET.get('page', 1)
                    posts_page = paginator.get_page(page)

                    context.update({
                        'posts_page': posts_page,
                        'paginator': paginator,
                        'posts_count': len(published_posts)
                    })

                    return context
            ---
    b.数据库连接和事务优化
        a.功能说明
            使用transaction.atomic()确保数据一致性,批量操作使用update()、bulk_create()减少SQL执行次数。
            合理配置数据库连接池参数,使用using()指定数据库,监控查询数量和执行时间,优化慢查询和高频操作。
        b.代码示例
            ---
            from django.db import transaction, connection
            from django.views.generic import View
            from django.http import JsonResponse
            from .models import Post, Comment, Tag

            class OptimizedBulkOperationView(View):
                """优化的批量操作视图"""

                def post(self, request):
                    action = request.POST.get('action')
                    post_ids = request.POST.getlist('post_ids')

                    if not post_ids:
                        return JsonResponse({'error': 'No posts selected'}, status=400)

                    try:
                        with transaction.atomic():  # 使用事务
                            if action == 'publish':
                                # 批量更新,减少SQL查询次数
                                Post.objects.filter(pk__in=post_ids).update(
                                    published=True,
                                    published_at=timezone.now()
                                )

                            elif action == 'delete':
                                # 先删除相关评论,再删除文章
                                Comment.objects.filter(post_id__in=post_ids).delete()
                                Post.objects.filter(pk__in=post_ids).delete()

                            elif action == 'add_tags':
                                tag_names = request.POST.getlist('tags')
                                tags = Tag.objects.filter(name__in=tag_names)

                                # 批量添加标签关系
                                for post in Post.objects.filter(pk__in=post_ids):
                                    post.tags.add(*tags)

                        return JsonResponse({'success': True, 'count': len(post_ids)})

                    except Exception as e:
                        return JsonResponse({'error': str(e)}, status=500)

                def get(self, request):
                    """显示批量操作页面"""
                    # 使用select_related优化查询
                    posts = Post.objects.select_related('author', 'category')

                    # 添加统计信息,避免后续查询
                    posts = posts.annotate(
                        comments_count=Count('comments', filter=Q(comments__approved=True)),
                        likes_count=Count('likes')
                    )

                    return render(request, 'blog/bulk_operations.html', {
                        'posts': posts
                    })

            class OptimizedImportView(View):
                """优化的数据导入视图"""

                def post(self, request):
                    try:
                        # 解析上传的文件
                        import_file = request.FILES['import_file']
                        import_data = json.loads(import_file.read().decode('utf-8'))

                        with transaction.atomic():
                            # 批量创建,减少数据库访问
                            posts_to_create = []
                            comments_to_create = []

                            for post_data in import_data:
                                # 创建文章对象(不立即保存)
                                post = Post(
                                    title=post_data['title'],
                                    content=post_data['content'],
                                    author_id=post_data['author_id'],
                                    category_id=post_data['category_id'],
                                    published=post_data.get('published', False)
                                )
                                posts_to_create.append(post)

                            # 批量创建文章
                            created_posts = Post.objects.bulk_create(posts_to_create, batch_size=100)

                            # 为创建的文章批量添加评论
                            for post, post_data in zip(created_posts, import_data):
                                if 'comments' in post_data:
                                    for comment_data in post_data['comments']:
                                        comments_to_create.append(
                                            Comment(
                                                post=post,
                                                content=comment_data['content'],
                                                user_id=comment_data['user_id'],
                                                approved=comment_data.get('approved', True)
                                            )
                                        )

                            # 批量创建评论
                            Comment.objects.bulk_create(comments_to_create, batch_size=200)

                        return JsonResponse({
                            'success': True,
                            'posts_created': len(created_posts),
                            'comments_created': len(comments_to_create)
                        })

                    except Exception as e:
                        return JsonResponse({'error': str(e)}, status=500)

                def get(self, request):
                    return render(request, 'blog/import_data.html')

            # 数据库连接优化配置
            class DatabaseOptimizedMixin:
                """数据库优化Mixin"""
                using = None  # 指定使用的数据库

                def get_queryset(self):
                    """使用指定的数据库"""
                    queryset = super().get_queryset()
                    if self.using:
                        queryset = queryset.using(self.using)
                    return queryset

                def form_valid(self, form):
                    """保存时使用指定数据库"""
                    if self.using:
                        form.instance.save(using=self.using)
                        return HttpResponseRedirect(self.get_success_url())
                    return super().form_valid(form)

            # 监控数据库查询
            class DatabaseQueryMonitorMixin:
                """数据库查询监控Mixin"""
                debug_queries = False

                def dispatch(self, request, *args, **kwargs):
                    if self.debug_queries:
                        from django.conf import settings
                        if settings.DEBUG:
                            from django.db import connection
                            initial_queries = len(connection.queries)

                    response = super().dispatch(request, *args, **kwargs)

                    if self.debug_queries and settings.DEBUG:
                        final_queries = len(connection.queries)
                        query_count = final_queries - initial_queries

                        if query_count > 50:  # 查询次数过多时警告
                            import logging
                            logger = logging.getLogger(__name__)
                            logger.warning(
                                f"High query count in {self.__class__.__name__}: {query_count} queries"
                            )

                        # 添加响应头信息
                        response['X-DB-Query-Count'] = str(query_count)

                    return response
            ---
    c.缓存策略
        a.功能说明
            视图级缓存使用cache_page装饰器缓存整个响应,片段缓存通过cache模板标签缓存部分内容,查询缓存存储查询结果减少数据库访问。
            根据内容更新频率、用户状态、请求参数设计缓存键和过期时间,平衡性能和数据新鲜度。
        b.代码示例
            ---
            from django.views.decorators.cache import cache_page, never_cache
            from django.core.cache import cache
            from django.utils.decorators import method_decorator
            from django.views.generic import ListView, DetailView
            import hashlib

            class SmartCachingMixin:
                """智能缓存Mixin"""
                cache_timeout = 300  # 5分钟
                cache_key_prefix = ''

                def get_cache_key(self, request):
                    """生成缓存键"""
                    # 包含URL、用户信息、查询参数
                    key_data = {
                        'url': request.get_full_path(),
                        'user_id': request.user.id if request.user.is_authenticated else None,
                        'get_params': dict(request.GET),
                        'lang': getattr(request, 'LANGUAGE_CODE', 'en')
                    }

                    key_string = json.dumps(key_data, sort_keys=True)
                    key_hash = hashlib.md5(key_string.encode()).hexdigest()
                    return f"{self.cache_key_prefix}{self.__class__.__name__}:{key_hash}"

                def should_cache(self, request, response):
                    """判断是否应该缓存"""
                    # 不缓存错误响应
                    if response.status_code >= 400:
                        return False

                    # 不缓存包含敏感信息的响应
                    if 'user' in str(response.content) and request.user.is_authenticated:
                        return False

                    # 不缓存POST/PUT/DELETE请求的响应
                    if request.method not in ['GET', 'HEAD']:
                        return False

                    return True

                def dispatch(self, request, *args, **kwargs):
                    cache_key = self.get_cache_key(request)

                    # 尝试从缓存获取
                    cached_response = cache.get(cache_key)
                    if cached_response:
                        return cached_response

                    # 执行视图
                    response = super().dispatch(request, *args, **kwargs)

                    # 缓存响应
                    if self.should_cache(request, response):
                        cache.set(cache_key, response, self.cache_timeout)

                    return response

            class CachedPostListView(SmartCachingMixin, ListView):
                """缓存的文章列表视图"""
                model = Post
                template_name = 'blog/post_list.html'
                context_object_name = 'posts'
                paginate_by = 10
                cache_key_prefix = 'post_list:'
                cache_timeout = 600  # 10分钟

                def get_queryset(self):
                    """获取查询集并应用查询缓存"""
                    cache_key = f"post_list_queryset:{self.request.GET.urlencode()}"
                    queryset = cache.get(cache_key)

                    if queryset is None:
                        queryset = Post.objects.filter(published=True).select_related(
                            'author', 'category'
                        ).prefetch_related('tags').order_by('-created_at')

                        # 缓存查询集
                        cache.set(cache_key, queryset, 300)  # 5分钟

                    return queryset

            @method_decorator(cache_page(60 * 15), name='dispatch')
            class HomePageView(ListView):
                """首页视图(固定缓存)"""
                model = Post
                template_name = 'blog/home.html'
                context_object_name = 'featured_posts'

                def get_queryset(self):
                    return Post.objects.filter(
                        featured=True,
                        published=True
                    ).select_related('author')[:10

            class ConditionalCacheMixin:
                """条件缓存Mixin"""
                cache_conditions = {}

                def get_cache_timeout(self, request):
                    """根据条件获取缓存时间"""
                    for condition, timeout in self.cache_conditions.items():
                        if condition(request):
                            return timeout
                    return 300  # 默认5分钟

                def dispatch(self, request, *args, **kwargs):
                    # 根据条件决定缓存策略
                    cache_timeout = self.get_cache_timeout(request)
                    cache_key = f"conditional_cache:{request.path}"

                    if cache_timeout > 0:
                        cached_response = cache.get(cache_key)
                        if cached_response:
                            return cached_response

                    response = super().dispatch(request, *args, **kwargs)

                    if cache_timeout > 0:
                        cache.set(cache_key, response, cache_timeout)

                    return response

            class DynamicCachedPostView(ConditionalCacheMixin, DetailView):
                """动态缓存文章视图"""
                model = Post
                template_name = 'blog/post_detail.html'
                context_object_name = 'post'

                cache_conditions = {
                    # 文章浏览次数小于1000时缓存15分钟
                    lambda request: getattr(request, 'post', None) and getattr(request.post, 'views_count', 0) < 1000: 900,
                    # 文章浏览次数大于1000时缓存5分钟
                    lambda request: getattr(request, 'post', None) and getattr(request.post, 'views_count', 0) >= 1000: 300,
                    # 匿名用户请求缓存10分钟
                    lambda request: not request.user.is_authenticated: 600,
                    # 已登录用户不缓存
                    lambda request: request.user.is_authenticated: 0,
                }

                def get_object(self, queryset=None):
                    post = super().get_object(queryset)
                    # 将文章对象添加到request中供条件判断使用
                    self.request.post = post
                    return post

            # 标签片段缓存
            from django.core.cache.utils import make_template_fragment_key

            class FragmentCachedMixin:
                """片段缓存Mixin"""
                fragment_cache_timeout = 300

                def render_fragment(self, fragment_name, context):
                    """渲染缓存片段"""
                    cache_key = make_template_fragment_key(fragment_name, context.values())

                    cached_content = cache.get(cache_key)
                    if cached_content:
                        return cached_content

                    # 渲染模板片段
                    template = get_template(f"blog/fragments/{fragment_name}.html")
                    content = template.render(context)

                    cache.set(cache_key, content, self.fragment_cache_timeout)
                    return content

            class CachedPostDetailView(FragmentCachedMixin, DetailView):
                """使用片段缓存的文章详情视图"""
                model = Post
                template_name = 'blog/post_detail_cached.html'
                context_object_name = 'post'

                def get_context_data(self, **kwargs):
                    context = super().get_context_data(**kwargs)
                    post = context['post']

                    # 缓存相关文章片段
                    context['related_posts_fragment'] = self.render_fragment(
                        'related_posts',
                        {'posts': post.get_related_posts()}
                    )

                    # 缓存评论片段
                    context['comments_fragment'] = self.render_fragment(
                        'comments',
                        {'comments': post.approved_comments.all()}
                    )

                    return context
            ---

5 模板与静态文件

5.1 模板引擎

01.DTL基础语法
    a.变量显示
        a.功能说明
            Django模板变量使用双大括号语法显示数据,支持对象属性访问、字典键访问和列表索引访问,可使用过滤器格式化输出和设置默认值。
        b.代码示例
            ---
            <!-- 基础变量显示 -->
            <h1>{{ post.title }}</h1>
            <p>作者:{{ post.author.username }}</p>
            <p>发布时间:{{ post.created_at|date:"Y-m-d H:i" }}</p>
            <!-- 对象和字典访问 -->
            <div class="post-category">
                分类:{{ post.category.name }}
            </div>
            <!-- 列表访问 -->
            <ul>
                <li>第一个标签:{{ post.tags.0.name }}</li>
                <li>标签数量:{{ post.tags|length }}</li>
            ---
    b.模板标签
        a.功能说明
            模板标签使用大括号百分号语法实现逻辑控制,包括if条件判断、for循环遍历、with变量赋值等,支持嵌套使用和复杂逻辑处理。
        b.代码示例
            ---
            <!-- 条件标签 -->
            {% if post.published %}
                <div class="published-post">
                    <h2>{{ post.title }}</h2>
                    <p>状态:已发布</p>
                </div>
            {% elif post.draft %}
                <div class="draft-post">
                    <h2>{{ post.title }}</h2>
                    <p>状态:草稿</p>
                </div>
            {% else %}
            ---

            {% block content %}
                <div class="post-detail">
                    <!-- 使用父级块内容 -->
                    {{ block.super }}

                    <!-- 添加新内容 -->
                    <h1>{{ post.title }}</h1>
                    <div class="post-content">
                        {{ post.content|safe }}
                    </div>
                </div>
            {% endblock %}

            {% block extra_css %}
                {{ block.super }}
                <link rel="stylesheet" href="{% static 'css/post_detail.css' %}">
            {% endblock %}

            {% block extra_js %}
                {{ block.super }}
                <script src="{% static 'js/post_detail.js' %}"></script>
            {% endblock %}
            ---
    c.内置过滤器
        a.功能说明
            内置过滤器用于格式化和转换变量输出,包括字符串处理、日期格式化、数值计算、列表操作等,可链式调用多个过滤器实现复杂转换。
        b.代码示例
            ---
            <!-- 字符串过滤器 -->
            <h2>{{ post.title|title }}</h2>
            <p>{{ post.content|truncatewords:50 }}</p>
            <p>{{ post.content|truncatechars:200 }}</p>
            <p>{{ user.email|lower }}</p>
            <p>{{ post.category.name|upper }}</p>
            <!-- 字符串格式化 -->
            <p>{{ post.content|wordcount }} 个单词</p>
            <p>{{ post.content|linebreaksbr }}</p>
            <p>{{ post.content|linebreaks }}</p>
            <p>{{ post.content|striptags }}</p>
            <!-- 链接和URL处理 -->
            ---

02.模板继承系统
    a.基础模板设计
        a.功能说明
            基础模板定义页面整体结构和公共部分,使用block标签定义可覆盖区域,子模板通过extends继承并重写block内容,实现模板复用和统一布局。
        b.代码示例
            ---
            <!-- templates/base.html -->
            <!DOCTYPE html>
            <html lang="zh-CN">
            <head>
                <meta charset="UTF-8">
                <meta name="viewport" content="width=device-width, initial-scale=1.0">
                {% block meta %}
                    <meta name="description" content="{{ meta_description|default:'Django博客网站' }}">
                    <meta name="keywords" content="{{ meta_keywords|default:'Django, 博客, Python' }}">
                    <meta name="author" content="{{ meta_author|default:'作者' }}">
                {% endblock %}
                <title>{% block title %}{{ site_name|default:'Django Blog' }}{% endblock %}</title>
            {% endblock %}
                </div></div>
                </nav>
                </header>
            {% endblock %}

            {% block messages %}
                <div class="container mt-3">
                    {% if messages %}
                            {% for message in messages %}
                                <div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
                                    {{ message }}
                                    <button type="button" class="close" data-dismiss="alert">
                                        <span>&times;</span>
                                    </button>
                                </div>
                            {% endfor %}
                        {% endif %}
                    </div>
                {% endblock %}

                {% block breadcrumbs %}
                    <div class="container mt-3">
                        {% block breadcrumb %}
                            <nav aria-label="breadcrumb">
                                <ol class="breadcrumb">
                                    <li class="breadcrumb-item">
                                        <a href="{% url 'home' %}">首页</a>
                                    </li>
                                    {% block breadcrumb_items %}
                                    {% endblock %}
                                </ol>
                            </nav>
                        {% endblock %}
                    </div>
                {% endblock %}

                {% block content %}
                    <main class="container my-4">
                        <div class="row">
                            {% block main_content %}
                                <div class="col-lg-8">
                                    <!-- 主要内容区域 -->
                                </div>
                            {% endblock %}

                            {% block sidebar %}
                                <div class="col-lg-4">
                                    <!-- 侧边栏区域 -->
                                </div>
                            {% endblock %}
                        </div>
                    </main>
                {% endblock %}

                {% block footer %}
                    <footer class="site-footer bg-light mt-5">
                        <div class="container">
                            <div class="row">
                                <div class="col-md-4">
                                    <h5>关于我们</h5>
                                    <p>{{ footer_about|default:'这是一个基于Django的博客网站' }}</p>
                                </div>
                                <div class="col-md-4">
                                    <h5>快速链接</h5>
                                    <ul class="list-unstyled">
                                        <li><a href="{% url 'blog:post_list' %}">所有文章</a></li>
                                        <li><a href="{% url 'categories' %}">分类目录</a></li>
                                        <li><a href="{% url 'tags' %}">标签云</a></li>
                                    </ul>
                                </div>
                                <div class="col-md-4">
                                    <h5>联系方式</h5>
                                    <p>{{ footer_contact|default:'联系邮箱:[email protected]' }}</p>
                                </div>
                            </div>
                            <hr>
                            <div class="text-center py-3">
                                <p>&copy; {% now "Y" %} {{ site_name|default:'Django Blog' }}.
                                   保留所有权利 |
                                   <a href="{% url 'privacy' %}">隐私政策</a> |
                                   <a href="{% url 'terms' %}">使用条款</a>
                                </p>
                            </div>
                        </div>
                    </footer>
                {% endblock %}

                {% block js %}
                    <!-- 基础JavaScript -->
                    <script src="{% static 'js/jquery.min.js' %}"></script>
                    <script src="{% static 'js/bootstrap.bundle.min.js' %}"></script>
                    <script src="{% static 'js/main.js' %}"></script>
                {% endblock %}

                {% block extra_js %}
                    <!-- 额外的JavaScript块,供子模板重写 -->
                {% endblock %}

                {% block analytics %}
                    <!-- 网站统计代码 -->
                    {% if google_analytics_id %}
                        <script async src="https://www.googletagmanager.com/gtag/js?id={{ google_analytics_id }}"></script>
                        <script>
                            window.dataLayer = window.dataLayer || [];
                            function gtag(){dataLayer.push(arguments);}
                            gtag('js', new Date());
                            gtag('config', '{{ google_analytics_id }}');
                        </script>
                    {% endif %}
                {% endblock %}
            </body>
            </html>
            ---
    b.子模板继承
        a.功能说明
            该示例展示了核心功能的实现方法,包含必要的配置和代码逻辑,可直接应用于实际项目开发。
        b.代码示例
            ---
            <!-- templates/blog/post_list.html -->
            {% extends 'base.html' %}
            {% block title %}
                文章列表 - {{ block.super }}
                {% endblock %}
                {% block meta_description %}
                {% endblock %}
                {% block meta_keywords %}
                {% endblock %}
                {% block breadcrumb_items %}
                    {{ block.super }}
                    <li class="breadcrumb-item active">文章列表</li>
                                            {{ post.category.name }}
                                        </a>
                                    </div>
                                {% endif %}
                                    {% if post.tags %}
                                        <div class="mb-3">
                                            {% for tag in post.tags %}
                                                <a href="{% url 'blog:tag_posts' tag.slug %}"
                                                   class="badge badge-light">#{{ tag.name }}</a>
                                            {% endfor %}
                                        </div>
                                    {% endif %}

                                    <p class="card-text">{{ post.content|striptags|truncatewords:30 }}</p>

                                    <div class="d-flex justify-content-between align-items-center">
                                        <a href="{% url 'blog:post_detail' post.slug %}" class="btn btn-primary">
                                            阅读全文
                                        </a>
                                        {% if user == post.author or user.is_staff %}
                                            <div>
                                                <a href="{% url 'blog:post_edit' post.id %}" class="btn btn-sm btn-outline-secondary">
                                                    <i class="fas fa-edit"></i> 编辑
                                                </a>
                                                <a href="{% url 'blog:post_delete' post.id %}" class="btn btn-sm btn-outline-danger">
                                                    <i class="fas fa-trash"></i> 删除
                                                </a>
                                            </div>
                                        {% endif %}
                                    </div>
                                </div>
                            </article>
                        {% endfor %}

                        <!-- 分页 -->
                        {% include 'common/pagination.html' with page_obj=posts_page page_range=paginator.page_range %}
                    {% else %}
                        <div class="text-center py-5">
                            <i class="fas fa-inbox fa-3x text-muted mb-3"></i>
                            <h4>暂无文章</h4>
                            <p class="text-muted">当前没有符合条件的文章</p>
                            {% if user.is_authenticated %}
                                <a href="{% url 'blog:post_create' %}" class="btn btn-primary">
                                    写第一篇文章
                                </a>
                            {% endif %}
                        </div>
                    {% endif %}
                </div>
            {% endblock %}

            {% block sidebar %}
                <div class="col-lg-4">
                    {% include 'blog/sidebar/search.html' %}
                    {% include 'blog/sidebar/categories.html' %}
                    {% include 'blog/sidebar/recent_posts.html' %}
                    {% include 'blog/sidebar/tags.html' %}
                    {% include 'blog/sidebar/archive.html' %}
                </div>
            {% endblock %}

            {% block extra_css %}
                {{ block.super }}
                <style>
                    .post-meta i {
                        width: 16px;
                        text-align: center;
                    }
                    .badge {
                        font-size: 0.75em;
                    }
                </style>
            {% endblock %}

            {% block extra_js %}
                {{ block.super }}
                <script>
                    // 自动提交搜索表单(选择分类或排序时)
                    $(document).ready(function() {
                        $('select[name="category"], select[name="sort"]').change(function() {
                            $(this).closest('form').submit();
                        });
                    });
                </script>
            {% endblock %}
            ---
    c.多层继承和包含
        a.功能说明
            该示例展示了核心功能的实现方法,包含必要的配置和代码逻辑,可直接应用于实际项目开发。
        b.代码示例
            ---
            <!-- templates/blog/base_blog.html -->
            {% extends 'base.html' %}
            {% block title %}
                {% block blog_title %}博客{% endblock %} - {{ block.super }}
            {% endblock %}
            {% block breadcrumb_items %}
                {{ block.super }}
                <li class="breadcrumb-item">
                    <a href="{% url 'blog:post_list' %}">博客</a>
                </li>
                {% block blog_breadcrumb_items %}{% endblock %}
                {% endblock %}
                        </div>
                    {% endif %}

                    <div class="post-content">
                        {{ post.content|safe }}
                    </div>

                    <footer class="post-footer mt-4 pt-4 border-top">
                        <!-- 文章操作按钮 -->
                        <div class="post-actions mb-3">
                            {% if user.is_authenticated %}
                                <button class="btn btn-outline-primary btn-sm like-btn"
                                        data-post-id="{{ post.id }}"
                                        {% if user in post.likes.all %}disabled{% endif %}>
                                    <i class="fas fa-heart"></i>
                                    {{ post.likes.count }} 点赞
                                </button>
                                <button class="btn btn-outline-secondary btn-sm bookmark-btn"
                                        data-post-id="{{ post.id }}"
                                        {% if user in post.bookmarks.all %}disabled{% endif %}>
                                    <i class="fas fa-bookmark"></i>
                                    收藏
                                </button>
                            {% endif %}

                            <div class="float-right">
                                <button class="btn btn-outline-info btn-sm share-btn">
                                    <i class="fas fa-share"></i> 分享
                                </button>
                            </div>
                        </div>

                        <!-- 作者信息 -->
                        <div class="author-info card">
                            <div class="card-body">
                                <div class="d-flex align-items-center">
                                    <div class="author-avatar mr-3">
                                        {% if post.author.profile.avatar %}
                                            <img src="{{ post.author.profile.avatar.url }}"
                                                 alt="{{ post.author.username }}"
                                                 class="rounded-circle" width="60" height="60">
                                        {% else %}
                                            <div class="rounded-circle bg-primary text-white d-flex align-items-center justify-content-center"
                                                 style="width: 60px; height: 60px;">
                                                {{ post.author.username.0|upper }}
                                            </div>
                                        {% endif %}
                                    </div>
                                    <div class="author-details">
                                        <h5 class="mb-1">
                                            <a href="{% url 'users:profile' post.author.id %}">
                                                {{ post.author.username }}
                                            </a>
                                        </h5>
                                        <p class="text-muted mb-0">
                                            发布了 {{ post.author.posts.count }} 篇文章
                                        </p>
                                        {% if post.author.profile.bio %}
                                            <p class="text-muted small mb-0">{{ post.author.profile.bio }}</p>
                                        {% endif %}
                                    </div>
                                </div>
                            </div>
                        </div>

                        <!-- 相关文章 -->
                        {% if related_posts %}
                            <div class="related-posts mt-4">
                                <h4>相关文章</h4>
                                <div class="row">
                                    {% for related_post in related_posts %}
                                        <div class="col-md-6 mb-3">
                                            <div class="card h-100">
                                                {% if related_post.featured_image %}
                                                    <img src="{{ related_post.featured_image.url }}"
                                                         class="card-img-top"
                                                         alt="{{ related_post.title }}"
                                                         style="height: 150px; object-fit: cover;">
                                                {% endif %}
                                                <div class="card-body d-flex flex-column">
                                                    <h6 class="card-title">
                                                        <a href="{% url 'blog:post_detail' related_post.slug %}"
                                                           class="text-decoration-none">
                                                            {{ related_post.title|truncatechars:50 }}
                                                        </a>
                                                    </h6>
                                                    <p class="card-text text-muted small">
                                                        {{ related_post.created_at|date:"Y年m月d日" }}
                                                    </p>
                                                </div>
                                            </div>
                                        </div>
                                    {% endfor %}
                                </div>
                            </div>
                        {% endif %}
                    </footer>
                </article>

                <!-- 评论区 -->
                {% include 'blog/comments/section.html' %}
            {% endblock %}

            {% block blog_sidebar %}
                {{ block.super }}
                <!-- 文章目录 -->
                {% include 'blog/sidebar/table_of_contents.html' %}
            {% endblock %}

            {% block extra_css %}
                {{ block.super }}
                <link rel="stylesheet" href="{% static 'css/pygments.css' %}">
                <style>
                    .post-content {
                        line-height: 1.8;
                        font-size: 16px;
                    }
                    .post-content h1,
                    .post-content h2,
                    .post-content h3,
                    .post-content h4,
                    .post-content h5,
                    .post-content h6 {
                        margin-top: 2rem;
                        margin-bottom: 1rem;
                    }
                    .post-content img {
                        max-width: 100%;
                        height: auto;
                        border-radius: 8px;
                        margin: 1rem 0;
                    }
                    .post-content pre {
                        background-color: #f8f9fa;
                        border-radius: 4px;
                        padding: 1rem;
                        overflow-x: auto;
                    }
                    .author-info .card {
                        background-color: #f8f9fa;
                    }
                </style>
            {% endblock %}

            {% block extra_js %}
                {{ block.super }}
                <script src="{% static 'js/post-detail.js' %}"></script>
            {% endblock %}
            ---

03.自定义模板标签和过滤器
    a.简单过滤器
        a.功能说明
            自定义过滤器通过register.filter装饰器注册函数,接收变量值和可选参数,返回处理后的结果,需在templatetags目录下创建模块并在模板中load加载。
        b.代码示例
            ---
            if len(value) <= length:
            text = re.sub(r'<[^>]+>', '', value)  # 移除HTML标签
            if len(text) <= length:
            highlighted = pattern.sub(f'<mark>{term}</mark>', text)
            return mark_safe(highlighted)

            @register.filter
            def user_avatar_url(user, size=50):
                """
                获取用户头像URL
                """
                if hasattr(user, 'profile') and user.profile.avatar:
                    return user.profile.avatar.url
                else:
                    # 生成默认头像
                    initials = user.username[:2].upper()
                    return f"https://ui-avatars.com/api/?name={initials}&size={size}&background=007bff&color=ffffff"

            @register.filter
            def add_css_class(field, css_class):
                """
                为表单字段添加CSS类
                """
                return field.as_widget(attrs={"class": css_class})

            @register.filter
            def star_rating(rating):
                """
                将数字评分转换为星级显示
                """
                full_stars = int(rating)
                half_star = 1 if rating % 1 >= 0.5 else 0
                empty_stars = 5 - full_stars - half_star

                stars_html = ''
                stars_html += '<i class="fas fa-star text-warning"></i>' * full_stars
                if half_star:
                    stars_html += '<i class="fas fa-star-half-alt text-warning"></i>'
                stars_html += '<i class="far fa-star text-warning"></i>' * empty_stars

                return mark_safe(f'<div class="star-rating">{stars_html}</div>')

            @register.filter
            def currency_format(amount, currency='CNY'):
                """
                货币格式化
                """
                currency_symbols = {
                    'CNY': '¥',
                    'USD': '$',
                    'EUR': '€',
                    'GBP': '£',
                    'JPY': '¥'
                }

                symbol = currency_symbols.get(currency, currency)
                if isinstance(amount, (int, float)):
                    return f"{symbol}{amount:,.2f}"
                return f"{symbol}{amount}"

            @register.filter
            def privacy_mask(value, mask_char='*'):
                """
                隐私信息遮罩
                """
                if not value:
                    return value

                value_str = str(value)
                if len(value_str) <= 3:
                    return mask_char * len(value_str)

                start = value_str[:2]
                middle = mask_char * (len(value_str) - 4)
                end = value_str[-2:]

                return f"{start}{middle}{end}"

            @register.filter
            def social_link(platform, username):
                """
                生成社交媒体链接
                """
                platforms = {
                    'twitter': 'https://twitter.com/',
                    'github': 'https://github.com/',
                    'linkedin': 'https://linkedin.com/in/',
                    'instagram': 'https://instagram.com/',
                    'facebook': 'https://facebook.com/',
                    'youtube': 'https://youtube.com/user/'
                }

                base_url = platforms.get(platform.lower())
                if base_url:
                    return mark_safe(f'<a href="{base_url}{username}" target="_blank" rel="noopener">{platform.title()}</a>')
                return username

            @register.filter
            def progress_bar(percentage, label=''):
                """
                生成进度条HTML
                """
                try:
                    percent = float(percentage)
                    if percent < 0:
                        percent = 0
                    elif percent > 100:
                        percent = 100
                except (ValueError, TypeError):
                    percent = 0

                # 根据百分比选择颜色
                if percent < 30:
                    color_class = 'bg-danger'
                elif percent < 70:
                    color_class = 'bg-warning'
                else:
                    color_class = 'bg-success'

                html = f'''
                <div class="progress">
                    <div class="progress-bar {color_class}" role="progressbar"
                         style="width: {percent}%" aria-valuenow="{percent}"
                         aria-valuemin="0" aria-valuemax="100">
                        {label} {percent}%
                    </div>
                </div>
                '''

                return mark_safe(html)

            @register.filter
            def reading_time(content):
                """
                估算文章阅读时间
                """
                if not content:
                    return '0分钟'

                # 移除HTML标签
                text = re.sub(r'<[^>]+>', '', str(content))
                word_count = len(text.split())

                # 假设每分钟阅读200个单词
                minutes = word_count / 200

                if minutes < 1:
                    return '不到1分钟'
                elif minutes < 60:
                    return f'{int(minutes)}分钟'
                else:
                    hours = int(minutes / 60)
                    remaining_minutes = int(minutes % 60)
                    if remaining_minutes == 0:
                        return f'{hours}小时'
                    else:
                        return f'{hours}小时{remaining_minutes}分钟'

            @register.filter
            def email_obfuscate(email):
                """
                邮箱地址混淆,防止垃圾邮件
                """
                if not email:
                    return ''

                # HTML实体编码
                encoded = ''
                for char in email:
                    encoded += f'&#{ord(char)};'

                return mark_safe(encoded)

            @register.filter
            def phone_format(phone):
                """
                电话号码格式化
                """
                # 移除所有非数字字符
                digits = re.sub(r'\D', '', str(phone))

                if len(digits) == 11:  # 中国手机号
                    return f'{digits[:3]}-{digits[3:7]}-{digits[7:]}'
                elif len(digits) == 10:  # 美国电话号
                    return f'({digits[:3]}) {digits[3:6]}-{digits[6:]}'
                else:
                    return phone
            ---
    b.简单标签
        a.功能说明
            自定义标签通过register.simple_tag或inclusion_tag创建,支持接收参数和上下文,可返回字符串或渲染模板,实现复杂的模板逻辑封装。
        b.代码示例
            ---
            # blog/templatetags/blog_tags.py
            from django import template
            from django.utils.safestring import mark_safe
            from django.urls import reverse
            from django.db.models import Count, Q
            from ..models import Post, Category, Tag
            import random

            register = template.Library()

            @register.simple_tag
            def latest_posts(count=5):
                获取最新文章
                posts = Post.objects.filter(published=True).order_by('-created_at')[:count]
                return posts

            ---
            def get_related_posts(post, count=3):
                """
                获取相关文章(基于分类和标签)
                """
                related_posts = Post.objects.filter(
                    published=True
                ).filter(
                    Q(category=post.category) |
                    Q(tags__in=post.tags.all())
                ).exclude(pk=post.pk).distinct()

                return related_posts[:count]

            @register.simple_tag(takes_context=True)
            def social_share_links(context, title, url):
                """
                生成社交媒体分享链接
                """
                request = context['request']
                full_url = request.build_absolute_uri(url) if url.startswith('/') else url

                links = {
                    'weibo': f'http://service.weibo.com/share/share.php?title={title}&url={full_url}',
                    'qq': f'https://connect.qq.com/widget/shareqq/index.html?title={title}&url={full_url}',
                    'wechat': f'#',  # 微信分享需要使用特殊实现
                    'twitter': f'https://twitter.com/intent/tweet?text={title}&url={full_url}',
                    'facebook': f'https://www.facebook.com/sharer/sharer.php?u={full_url}',
                    'linkedin': f'https://www.linkedin.com/sharing/share-offsite/?url={full_url}'
                }

                return links

            @register.simple_tag
            def get_navigation_menu():
                """
                获取导航菜单
                """
                menu_items = [
                    {'title': '首页', 'url': reverse('home'), 'icon': 'fas fa-home'},
                    {'title': '博客', 'url': reverse('blog:post_list'), 'icon': 'fas fa-blog'},
                    {'title': '分类', 'url': reverse('blog:categories'), 'icon': 'fas fa-folder'},
                    {'title': '标签', 'url': reverse('blog:tags'), 'icon': 'fas fa-tags'},
                    {'title': '归档', 'url': reverse('blog:archive'), 'icon': 'fas fa-calendar'},
                    {'title': '关于', 'url': reverse('about'), 'icon': 'fas fa-info-circle'},
                    {'title': '联系', 'url': reverse('contact'), 'icon': 'fas fa-envelope'},
                ]

                return menu_items

            @register.simple_tag
            def get_breadcrumb_items(current_page=None, **kwargs):
                """
                生成面包屑导航
                """
                items = [
                    {'title': '首页', 'url': reverse('home'), 'active': False}
                ]

                if current_page == 'post_list':
                    items.append({'title': '博客', 'url': reverse('blog:post_list'), 'active': True})
                elif current_page == 'post_detail' and 'post' in kwargs:
                    post = kwargs['post']
                    items.append({'title': '博客', 'url': reverse('blog:post_list'), 'active': False})
                    if post.category:
                        items.append({
                            'title': post.category.name,
                            'url': reverse('blog:category_posts', args=[post.category.slug]),
                            'active': False
                        })
                    items.append({'title': post.title, 'url': None, 'active': True})

                elif current_page == 'category_posts' and 'category' in kwargs:
                    category = kwargs['category']
                    items.append({'title': '博客', 'url': reverse('blog:post_list'), 'active': False})
                    items.append({'title': category.name, 'url': None, 'active': True})

                return items

            @register.simple_tag
            def generate_qr_code(url, size=150):
                """
                生成二维码(需要安装qrcode库)
                """
                try:
                    import qrcode
                    from io import BytesIO
                    import base64

                    qr = qrcode.QRCode(
                        version=1,
                        error_correction=qrcode.constants.ERROR_CORRECT_L,
                        box_size=10,
                        border=4,
                    )
                    qr.add_data(url)
                    qr.make(fit=True)

                    img = qr.make_image(fill_color="black", back_color="white")
                    buffer = BytesIO()
                    img.save(buffer, format='PNG')
                    img_str = base64.b64encode(buffer.getvalue()).decode()

                    return mark_safe(f'<img src="data:image/png;base64,{img_str}" alt="QR Code" width="{size}" height="{size}">')

                except ImportError:
                    return mark_safe(f'<a href="{url}">{url}</a>')

            @register.simple_tag(takes_context=True)
            def get_user_notifications(context):
                """
                获取用户通知
                """
                user = context['request'].user
                if not user.is_authenticated:
                    return []

                # 假设有Notification模型
                from ..models import Notification
                notifications = Notification.objects.filter(
                    user=user,
                    read=False
                ).order_by('-created_at')[:5]

                return notifications

            @register.simple_tag
            def get_site_statistics():
                """
                获取网站统计信息
                """
                stats = {
                    'total_posts': Post.objects.filter(published=True).count(),
                    'total_categories': Category.objects.count(),
                    'total_tags': Tag.objects.count(),
                    'total_views': Post.objects.aggregate(
                        total_views=Count('views_count')
                    )['total_views'] or 0,
                }

                return stats

            @register.simple_tag
            def generate_table_of_contents(content):
                """
                生成文章目录
                """
                import re
                from django.utils.html import escape

                # 提取标题
                headings = re.findall(r'<h([1-6])[^>]*>(.*?)</h[1-6]>', content)

                toc_items = []
                for level, title in headings:
                    # 清理标题文本
                    clean_title = re.sub(r'<[^>]+>', '', title)
                    # 生成锚点ID
                    anchor_id = re.sub(r'[^a-zA-Z0-9\u4e00-\u9fff]+', '-', clean_title.lower()).strip('-')

                    toc_items.append({
                        'level': int(level),
                        'title': clean_title,
                        'anchor_id': anchor_id
                    })

                return toc_items

            @register.simple_tag
            def generate_rating_stars(rating, max_rating=5):
                """
                生成评分星星HTML
                """
                try:
                    rating = float(rating)
                    max_rating = int(max_rating)
                except (ValueError, TypeError):
                    return ''

                stars_html = ''
                full_stars = int(rating)
                has_half_star = (rating - full_stars) >= 0.5
                empty_stars = max_rating - full_stars - (1 if has_half_star else 0)

                # 实心星星
                stars_html += '<i class="fas fa-star text-warning"></i>' * full_stars

                # 半星
                if has_half_star:
                    stars_html += '<i class="fas fa-star-half-alt text-warning"></i>'

                # 空心星星
                stars_html += '<i class="far fa-star text-warning"></i>' * empty_stars

                return mark_safe(f'<div class="rating-stars" title="{rating}/{max_rating}">{stars_html}</div>')

            @register.simple_tag
            def get_weather_info(city):
                """
                获取天气信息(示例实现)
                """
                # 这里应该调用天气API
                # 简化的实现,返回模拟数据
                weather_data = {
                    'temperature': random.randint(15, 30),
                    'condition': random.choice(['晴', '多云', '阴', '小雨']),
                    'humidity': random.randint(40, 80),
                    'wind_speed': random.randint(5, 20)
                }

                return weather_data
            ---

5.2 模板上下文处理器

01.内置上下文处理器
    a.request处理器
        a.功能说明
            该示例展示了核心功能的实现方法,包含必要的配置和代码逻辑,可直接应用于实际项目开发。
        b.代码示例
            ---
            <!-- 在settings.py中启用 -->
            <!-- 在模板中使用request对象 -->
            <!DOCTYPE html>
            <html lang="{{ request.LANGUAGE_CODE|default:'zh-CN' }}">
            <head>
                <meta charset="UTF-8">
                <title>{% block title %}{{ site_name }}{% endblock %}</title>
                <!-- 获取当前URL和查询参数 -->
                {% block meta %}
                    <meta name="description" content="{{ request.get_full_path }}">
                    <link rel="canonical" href="{{ request.build_absolute_uri }}">
                {% endblock %}
            ---
    b.auth处理器
        a.功能说明
            该示例展示了核心功能的实现方法,包含必要的配置和代码逻辑,可直接应用于实际项目开发。
        b.代码示例
            ---
            <!-- settings.py中启用auth上下文处理器 -->
            <!-- 在模板中使用认证上下文 -->
            <nav class="user-navigation">
                {% if user.is_authenticated %}
                    <div class="user-profile">
                        <!-- 用户头像 -->
                        <div class="user-avatar">
                            {% if user.profile.avatar %}
                                <img src="{{ user.profile.avatar.url }}" alt="{{ user.username }}">
                            {% else %}
                                <div class="default-avatar">
                                    {{ user.username|first|upper }}
                                    查看详情
                                </a>

                                {% if perms.blog.change_post %}
                                    <a href="{% url 'blog:post_edit' post.id %}" class="btn btn-secondary">
                                        编辑
                                    </a>
                                {% endif %}

                                {% if perms.blog.delete_post %}
                                    <a href="{% url 'blog:post_delete' post.id %}" class="btn btn-danger">
                                        删除
                                    </a>
                                {% endif %}

                                <!-- 检查特定对象权限 -->
                                {% if perms.blog.can_moderate_post %}
                                    {% if post.published %}
                                        <button class="btn btn-warning" onclick="unpublishPost({{ post.id }})">
                                            下架文章
                                        </button>
                                    {% else %}
                                        <button class="btn btn-success" onclick="publishPost({{ post.id }})">
                                            发布文章
                                        </button>
                                    {% endif %}
                                {% endif %}
                            </div>
                        </article>
                    {% endfor %}
                {% else %}
                    <div class="alert alert-warning">
                        <h4>权限不足</h4>
                        <p>您没有查看文章的权限。请联系管理员获取相应权限。</p>
                    </div>
                {% endif %}
            </div>

            <!-- 消息显示 -->
            {% if messages %}
                <div class="messages-container">
                    {% for message in messages %}
                        <div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
                            <!-- 消息级别图标 -->
                            {% if message.level == DEFAULT_MESSAGE_LEVELS.SUCCESS %}
                                <i class="fas fa-check-circle"></i>
                            {% elif message.level == DEFAULT_MESSAGE_LEVELS.ERROR %}
                                <i class="fas fa-exclamation-circle"></i>
                            {% elif message.level == DEFAULT_MESSAGE_LEVELS.WARNING %}
                                <i class="fas fa-exclamation-triangle"></i>
                            {% elif message.level == DEFAULT_MESSAGE_LEVELS.INFO %}
                                <i class="fas fa-info-circle"></i>
                            {% endif %}

                            {{ message }}

                            <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                                <span aria-hidden="true">&times;</span>
                            </button>
                        </div>
                    {% endfor %}
                </div>
            {% endif %}

            <!-- 基于用户组显示不同内容 -->
            <aside class="sidebar">
                {% if user.is_authenticated %}
                    {% if 'authors' in user.groups.all %}
                        <div class="widget author-tools">
                            <h3>作者工具</h3>
                            <ul>
                                <li><a href="{% url 'blog:post_create' %}">写新文章</a></li>
                                <li><a href="{% url 'blog:drafts' %}">草稿箱 ({{ user.posts.filter(published=False).count }})</a></li>
                                <li><a href="{% url 'blog:my_posts' %}">我的文章</a></li>
                            </ul>
                        </div>
                    {% endif %}

                    {% if user.is_staff %}
                        <div class="widget admin-tools">
                            <h3>管理工具</h3>
                            <ul>
                                <li><a href="{% url 'admin:index' %}">管理后台</a></li>
                                <li><a href="{% url 'admin:blog_post_changelist' %}">文章管理</a></li>
                                <li><a href="{% url 'admin:auth_user_changelist' %}">用户管理</a></li>
                            </ul>
                        </div>
                    {% endif %}
                {% endif %}
            </aside>
            ---
    c.debug处理器
        a.功能说明
            该示例展示了核心功能的实现方法,包含必要的配置和代码逻辑,可直接应用于实际项目开发。
        b.代码示例
            ---
            <!-- 仅在DEBUG=True时显示调试信息 -->
            {% if debug %}
                <div class="debug-panel" id="debug-panel">
                    <button class="debug-toggle" onclick="toggleDebugPanel()">
                        调试面板 ({{ sql_queries|length }} 查询)
                    </button>
                    <div class="debug-content" style="display: none;">
                        <!-- 请求信息 -->
                        <section class="debug-section">
                            <h4>请求信息</h4>
                            <table class="debug-table">
                                <tr>
                                    {% endfor %}
                                </div>
                            {% else %}
                                <p>没有SQL查询记录</p>
                            {% endif %}
                        </section>

                        <!-- 模板信息 -->
                        <section class="debug-section">
                            <h4>模板信息</h4>
                            <table class="debug-table">
                                <tr>
                                    <th>模板引擎</th>
                                    <td>Django Template Language</td>
                                </tr>
                                <tr>
                                    <th>当前模板</th>
                                    <td>{{ template_name|default:"未知" }}</td>
                                </tr>
                            </table>
                        </section>

                        <!-- 设置信息 -->
                        <section class="debug-section">
                            <h4>设置信息</h4>
                            <table class="debug-table">
                                <tr>
                                    <th>DEBUG模式</th>
                                    <td>{{ debug }}</td>
                                </tr>
                                <tr>
                                    <th>数据库引擎</th>
                                    <td>{{ DATABASES.default.ENGINE }}</td>
                                </tr>
                                <tr>
                                    <th>缓存后端</th>
                                    <td>{{ CACHES.default.BACKEND }}</td>
                                </tr>
                                <tr>
                                    <th>语言代码</th>
                                    <td>{{ LANGUAGE_CODE }}</td>
                                </tr>
                                <tr>
                                    <th>时区</th>
                                    <td>{{ TIME_ZONE }}</td>
                                </tr>
                            </table>
                        </section>

                        <!-- 上下文变量 -->
                        <section class="debug-section">
                            <h4>上下文变量</h4>
                            <div class="context-vars">
                                {% for key, value in context_data.items %}
                                    <div class="context-var">
                                        <strong>{{ key }}:</strong>
                                        {% if value %}
                                            {{ value|truncatechars:100 }}
                                            {% if value|length > 100 %}...{% endif %}
                                        {% else %}
                                            None
                                        {% endif %}
                                    </div>
                                {% endfor %}
                            </div>
                        </section>
                    </div>
                </div>

                <style>
                    .debug-panel {
                        position: fixed;
                        top: 10px;
                        right: 10px;
                        z-index: 9999;
                        background: #fff;
                        border: 1px solid #ddd;
                        border-radius: 4px;
                        box-shadow: 0 2px 10px rgba(0,0,0,0.1);
                        max-width: 500px;
                    }

                    .debug-toggle {
                        background: #007bff;
                        color: white;
                        border: none;
                        padding: 10px 15px;
                        cursor: pointer;
                        border-radius: 4px 4px 0 0;
                    }

                    .debug-content {
                        max-height: 500px;
                        overflow-y: auto;
                        padding: 15px;
                    }

                    .debug-section {
                        margin-bottom: 20px;
                    }

                    .debug-section h4 {
                        margin-top: 0;
                        border-bottom: 1px solid #eee;
                        padding-bottom: 5px;
                    }

                    .debug-table {
                        width: 100%;
                        border-collapse: collapse;
                    }

                    .debug-table th,
                    .debug-table td {
                        padding: 5px;
                        border: 1px solid #ddd;
                        text-align: left;
                    }

                    .debug-table th {
                        background: #f8f9fa;
                        width: 120px;
                    }

                    .query-item {
                        margin-bottom: 10px;
                        border: 1px solid #eee;
                        border-radius: 4px;
                        overflow: hidden;
                    }

                    .query-header {
                        background: #f8f9fa;
                        padding: 8px;
                        display: flex;
                        justify-content: space-between;
                        align-items: center;
                    }

                    .query-number {
                        font-weight: bold;
                        background: #007bff;
                        color: white;
                        padding: 2px 6px;
                        border-radius: 3px;
                        font-size: 12px;
                    }

                    .query-time {
                        color: #dc3545;
                        font-family: monospace;
                    }

                    .query-sql {
                        padding: 8px;
                        background: #f8f9fa;
                        font-family: monospace;
                        font-size: 12px;
                        white-space: pre-wrap;
                    }

                    .query-summary {
                        background: #e9ecef;
                        padding: 10px;
                        border-radius: 4px;
                        margin-bottom: 10px;
                    }

                    .context-var {
                        padding: 5px;
                        border-left: 3px solid #007bff;
                        margin-bottom: 5px;
                    }
                </style>

                <script>
                    function toggleDebugPanel() {
                        var content = document.querySelector('.debug-content');
                        content.style.display = content.style.display === 'none' ? 'block' : 'none';
                    }
                </script>
            {% endif %}
            ---

02.自定义上下文处理器
    a.创建自定义处理器
        a.功能说明
            该示例展示了核心功能的实现方法,包含必要的配置和代码逻辑,可直接应用于实际项目开发。
        b.代码示例
            ---
            # blog/context_processors.py
            from django.conf import settings
            from .models import Category, Tag, Post
            from django.db.models import Count, Q

            def site_info_context(request):
                return {
                    'site_name': getattr(settings, 'SITE_NAME', 'Django Blog'),
                    'site_description': getattr(settings, 'SITE_DESCRIPTION', '基于Django的博客网站'),
                    'site_keywords': getattr(settings, 'SITE_KEYWORDS', 'Django, 博客, Python'),
                    'site_author': getattr(settings, 'SITE_AUTHOR', 'Author'),
                    'site_logo_url': getattr(settings, 'SITE_LOGO_URL', None),
                    'contact_email': getattr(settings, 'CONTACT_EMAIL', '[email protected]'),
                    'ga_tracking_id': getattr(settings, 'GA_TRACKING_ID', None),
                }
                    'linkedin': getattr(settings, 'SOCIAL_LINKEDIN', ''),
                    'instagram': getattr(settings, 'SOCIAL_INSTAGRAM', ''),
                    'youtube': getattr(settings, 'SOCIAL_YOUTUBE', ''),
                    'facebook': getattr(settings, 'SOCIAL_FACEBOOK', ''),
                }

                # 只返回配置了链接的社交媒体
                enabled_social_links = {
                    platform: url for platform, url in social_links.items()
                    if url
                }

                return {'social_links': enabled_social_links}

            def recent_content_context(request):
                """最近内容上下文处理器"""
                # 最新文章
                recent_posts = Post.objects.filter(published=True).order_by('-created_at')[:5]

                # 最新评论(需要Comment模型)
                try:
                    from .models import Comment
                    recent_comments = Comment.objects.filter(
                        approved=True
                    ).select_related('user', 'post').order_by('-created_at')[:5]
                except ImportError:
                    recent_comments = []

                # 热门文章
                popular_posts = Post.objects.filter(
                    published=True
                ).order_by('-views_count')[:5]

                return {
                    'recent_posts': recent_posts,
                    'recent_comments': recent_comments,
                    'popular_posts': popular_posts,
                }

            def user_dashboard_context(request):
                """用户仪表板上下文处理器"""
                if not request.user.is_authenticated:
                    return {}

                user = request.user

                # 用户统计信息
                dashboard_stats = {
                    'my_posts_count': Post.objects.filter(author=user).count(),
                    'published_posts_count': Post.objects.filter(
                        author=user, published=True
                    ).count(),
                    'draft_posts_count': Post.objects.filter(
                        author=user, published=False
                    ).count(),
                    'total_views': Post.objects.filter(
                        author=user, published=True
                    ).aggregate(total=Count('views_count'))['total'] or 0,
                }

                # 用户权限
                user_permissions = {
                    'can_create_post': user.has_perm('blog.add_post'),
                    'can_edit_post': user.has_perm('blog.change_post'),
                    'can_delete_post': user.has_perm('blog.delete_post'),
                    'can_moderate': user.has_perm('blog.can_moderate_post'),
                    'is_author': user.groups.filter(name='authors').exists(),
                    'is_editor': user.groups.filter(name='editors').exists(),
                }

                return {
                    'user_dashboard_stats': dashboard_stats,
                    'user_permissions': user_permissions,
                }

            def theme_context(request):
                """主题和样式上下文处理器"""
                # 从session或cookie获取用户主题偏好
                theme = request.session.get('theme', request.COOKIES.get('theme', 'light'))

                # 检测设备类型
                user_agent = request.META.get('HTTP_USER_AGENT', '').lower()
                is_mobile = 'mobile' in user_agent or 'android' in user_agent
                is_tablet = 'tablet' in user_agent or 'ipad' in user_agent

                return {
                    'theme': theme,
                    'is_mobile': is_mobile,
                    'is_tablet': is_tablet,
                    'is_desktop': not is_mobile and not is_tablet,
                }

            def analytics_context(request):
                """分析追踪上下文处理器"""
                # 当前页面信息用于分析
                page_info = {
                    'title': getattr(request, 'page_title', ''),
                    'category': getattr(request, 'page_category', ''),
                    'tags': getattr(request, 'page_tags', ''),
                }

                # 用户行为追踪数据
                tracking_data = {
                    'page_type': getattr(request, 'page_type', 'unknown'),
                    'content_id': getattr(request, 'content_id', None),
                    'author_id': getattr(request, 'author_id', None),
                }

                return {
                    'analytics_page': page_info,
                    'analytics_tracking': tracking_data,
                }

            def seo_context(request):
                """SEO优化上下文处理器"""
                # 基础SEO信息
                seo_data = {
                    'canonical_url': request.build_absolute_uri(),
                    'og_type': 'website',
                    'og_site_name': getattr(settings, 'SITE_NAME', 'Django Blog'),
                    'twitter_card': 'summary_large_image',
                    'twitter_site': '@' + getattr(settings, 'TWITTER_HANDLE', ''),
                }

                # 根据页面类型调整SEO信息
                path = request.path

                if path.startswith('/blog/'):
                    if 'category' in path:
                        seo_data.update({
                            'og_type': 'website',
                            'page_type': 'category'
                        })
                    elif 'tag' in path:
                        seo_data.update({
                            'og_type': 'website',
                            'page_type': 'tag'
                        })
                    elif 'post' in path:
                        seo_data.update({
                            'og_type': 'article',
                            'page_type': 'article'
                        })

                return {'seo_data': seo_data}

            def notification_context(request):
                """通知上下文处理器"""
                if not request.user.is_authenticated:
                    return {}

                # 获取用户未读通知
                try:
                    from .models import Notification
                    notifications = Notification.objects.filter(
                        user=request.user,
                        is_read=False
                    ).order_by('-created_at')[:5]

                    notification_count = Notification.objects.filter(
                        user=request.user,
                        is_read=False
                    ).count()

                except ImportError:
                    notifications = []
                    notification_count = 0

                return {
                    'user_notifications': notifications,
                    'notification_count': notification_count,
                }

            # 在settings.py中注册自定义上下文处理器
            TEMPLATES = [
                {
                    'BACKEND': 'django.template.backends.django.DjangoTemplates',
                    'DIRS': [BASE_DIR / 'templates'],
                    'APP_DIRS': True,
                    'OPTIONS': {
                        'context_processors': [
                            'django.template.context_processors.debug',
                            'django.template.context_processors.request',
                            'django.contrib.auth.context_processors.auth',
                            'django.contrib.messages.context_processors.messages',

                            # 自定义上下文处理器
                            'blog.context_processors.site_info_context',
                            'blog.context_processors.navigation_context',
                            'blog.context_processors.blog_stats_context',
                            'blog.context_processors.social_links_context',
                            'blog.context_processors.recent_content_context',
                            'blog.context_processors.user_dashboard_context',
                            'blog.context_processors.theme_context',
                            'blog.context_processors.analytics_context',
                            'blog.context_processors.seo_context',
                            'blog.context_processors.notification_context',
                        ],
                    },
                },
            ]
            ---
    b.条件性上下文处理
        a.功能说明
            该示例展示了核心功能的实现方法,包含必要的配置和代码逻辑,可直接应用于实际项目开发。
        b.代码示例
            ---
            # blog/context_processors.py (续)
            from django.core.cache import cache
            from django.utils import timezone

            def conditional_category_context(request):
                # 只在需要分类信息的页面加载分类
                paths_need_categories = [
                    '/blog/',
                    '/categories/',
                    '/tags/',
                    '/admin/blog/',
                ]

                if not any(request.path.startswith(path) for path in paths_need_categories):
                    return {}

                # 根据用户角色生成不同菜单
                user_menu_items = []

                # 基础菜单项
                user_menu_items.extend([
                    {'title': '个人资料', 'url': 'users:profile', 'icon': 'fas fa-user'},
                    {'title': '设置', 'url': 'users:settings', 'icon': 'fas fa-cog'},
                ])

                # 作者菜单
                if request.user.groups.filter(name='authors').exists():
                    user_menu_items.extend([
                        {'title': '我的文章', 'url': 'blog:my_posts', 'icon': 'fas fa-list'},
                        {'title': '写文章', 'url': 'blog:post_create', 'icon': 'fas fa-plus'},
                    ])

                # 管理员菜单
                if request.user.is_staff:
                    user_menu_items.append({
                        'title': '管理后台',
                        'url': 'admin:index',
                        'icon': 'fas fa-tachometer-alt'
                    })

                return {'user_menu_items': user_menu_items}

            def conditional_recent_activity_context(request):
                """条件性最近活动上下文处理器"""
                if not request.user.is_authenticated:
                    return {}

                # 只在仪表板页面显示最近活动
                if not request.path.startswith('/dashboard/') and not request.path.startswith('/profile/'):
                    return {}

                try:
                    # 获取用户最近的活动
                    recent_posts = Post.objects.filter(
                        author=request.user
                    ).order_by('-created_at')[:3]

                    recent_comments = Comment.objects.filter(
                        user=request.user
                    ).order_by('-created_at')[:3]

                    return {
                        'recent_user_posts': recent_posts,
                        'recent_user_comments': recent_comments,
                    }

                except ImportError:
                    return {}

            def performance_optimized_context(request):
                """性能优化的上下文处理器"""
                context = {}

                # 使用请求级缓存避免重复计算
                cache_key_prefix = f"context_{request.user.id}_{request.path}"

                # 只对GET请求进行缓存
                if request.method == 'GET':
                    site_stats_key = f"{cache_key_prefix}_site_stats"
                    site_stats = cache.get(site_stats_key)

                    if site_stats is None:
                        site_stats = {
                            'total_posts': Post.objects.filter(published=True).count(),
                            'online_users': get_online_users_count(),  # 假设有这个函数
                        }
                        cache.set(site_stats_key, site_stats, 60)  # 缓存1分钟

                    context['site_stats'] = site_stats

                # 根据设备类型返回不同数据
                user_agent = request.META.get('HTTP_USER_AGENT', '').lower()
                if 'mobile' in user_agent:
                    context['is_mobile'] = True
                    context['template_suffix'] = '_mobile'
                else:
                    context['is_mobile'] = False
                    context['template_suffix'] = ''

                return context

            def security_context(request):
                """安全相关的上下文处理器"""
                security_data = {}

                # CSRF令牌(已经默认包含,但可以在这里处理特殊情况)
                security_data['csrf_token'] = request.META.get('CSRF_COOKIE')

                # 内容安全策略
                if getattr(settings, 'SECURE_CONTENT_TYPE_NOSNIFF', True):
                    security_data['content_security_policy'] = True

                # HTTPS重定向状态
                security_data['is_secure'] = request.is_secure()

                # 检测可疑请求
                suspicious_indicators = [
                    request.META.get('HTTP_X_FORWARDED_FOR') != request.META.get('REMOTE_ADDR'),
                    len(request.GET.keys()) > 20,  # 过多GET参数
                    len(request.POST.keys()) > 20,  # 过多POST参数
                ]

                security_data['is_suspicious'] = any(suspicious_indicators)

                return {'security': security_data}

            def multilingual_context(request):
                """多语言支持上下文处理器"""
                if not getattr(settings, 'USE_I18N', False):
                    return {}

                from django.utils import translation
                from django.conf import settings

                # 当前语言信息
                current_language = translation.get_language()
                available_languages = [
                    (code, name) for code, name in settings.LANGUAGES
                ]

                # 语言切换URL
                language_urls = {}
                for code, _ in settings.LANGUAGES:
                    with translation.override(code):
                        language_urls[code] = request.path

                return {
                    'current_language': current_language,
                    'available_languages': available_languages,
                    'language_urls': language_urls,
                    'use_i18n': True,
                }
            ---

6 表单与认证

6.1 Django表单系统

01.表单基础
    a.Form类定义
        a.功能说明
            该示例展示了核心功能的实现方法,包含必要的配置和代码逻辑,可直接应用于实际项目开发。
        b.代码示例
            ---
            # forms.py
            from django import forms
            from django.core.validators import RegexValidator
            from django.utils.translation import gettext_lazy as _

            class ContactForm(forms.Form):
                name = forms.CharField(
                    label=_("姓名"),
                    max_length=100,
                    widget=forms.TextInput(attrs={
                        'class': 'form-control',
                        'placeholder': _('请输入您的姓名')
                    }),
                    error_messages={
                        'required': _('请输入您的姓名'),
                        'max_length': _('姓名不能超过100个字符')
                    email = self.cleaned_data.get('email')
                    if email and 'example.com' in email:
                        raise forms.ValidationError(
                            _('请使用真实的邮箱地址,不要使用example.com域名')
                        )
                    return email

                def clean_message(self):
                    """自定义消息验证"""
                    message = self.cleaned_data.get('message')
                    if message and len(message) < 10:
                        raise forms.ValidationError(
                            _('消息内容不能少于10个字符')
                        )
                    return message

                def clean(self):
                    """表单整体验证"""
                    cleaned_data = super().clean()
                    email = cleaned_data.get('email')
                    phone = cleaned_data.get('phone')
                    contact_preference = cleaned_data.get('contact_preference')

                    # 检查联系方式的完整性
                    if contact_preference == 'email' and not email:
                        raise forms.ValidationError(
                            _('选择邮箱联系方式时必须提供邮箱地址')
                        )

                    if contact_preference == 'phone' and not phone:
                        raise forms.ValidationError(
                            _('选择电话联系方式时必须提供手机号码')
                        )

                    return cleaned_data

            # 视图中使用表单
            def contact_view(request):
                if request.method == 'POST':
                    form = ContactForm(request.POST, request.FILES)
                    if form.is_valid():
                        # 处理表单数据
                        name = form.cleaned_data['name']
                        email = form.cleaned_data['email']
                        subject = form.cleaned_data['subject']
                        message = form.cleaned_data['message']
                        phone = form.cleaned_data['phone']
                        contact_preference = form.cleaned_data['contact_preference']
                        attachment = form.cleaned_data.get('attachment')

                        # 发送邮件
                        try:
                            send_contact_email(
                                name=name,
                                email=email,
                                subject=subject,
                                message=message,
                                phone=phone,
                                contact_preference=contact_preference,
                                attachment=attachment
                            )

                            messages.success(
                                request,
                                _('您的消息已成功发送,我们会尽快回复您!')
                            )
                            return redirect('contact_success')

                        except Exception as e:
                            messages.error(
                                request,
                                _('发送消息时出现错误,请稍后重试')
                            )
                            logger.error(f"Contact form error: {str(e)}")

                else:
                    form = ContactForm()

                return render(request, 'contact/form.html', {'form': form})
            ---
    b.ModelForm使用
        a.功能说明
            该示例展示了核心功能的实现方法,包含必要的配置和代码逻辑,可直接应用于实际项目开发。
        b.代码示例
            ---
            # models.py
            from django.db import models
            from django.contrib.auth.models import User

            class BlogPost(models.Model):
                STATUS_CHOICES = [
                    ('draft', '草稿'),
                    ('published', '已发布'),
                    ('archived', '已归档'),
                ]

                title = models.CharField('标题', max_length=200)
                slug = models.SlugField('URL别名', unique=True)
                content = models.TextField('内容')
                excerpt = models.TextField('摘要', blank=True)
                author = models.ForeignKey(
                    widgets = {
                        'title': forms.TextInput(attrs={
                            'class': 'form-control',
                            'placeholder': '请输入文章标题'
                        }),
                        'slug': forms.TextInput(attrs={
                            'class': 'form-control',
                            'placeholder': '自动生成或手动输入URL别名'
                        }),
                        'content': forms.Textarea(attrs={
                            'class': 'form-control content-editor',
                            'rows': 20,
                            'placeholder': '请输入文章内容,支持Markdown格式'
                        }),
                        'category': forms.Select(attrs={
                            'class': 'form-control'
                        }),
                        'featured_image': forms.FileInput(attrs={
                            'class': 'form-control-file',
                            'accept': 'image/*'
                        }),
                        'status': forms.Select(attrs={
                            'class': 'form-control'
                        }),
                        'is_featured': forms.CheckboxInput(attrs={
                            'class': 'form-check-input'
                        }),
                        'meta_title': forms.TextInput(attrs={
                            'class': 'form-control',
                            'placeholder': '留空则使用文章标题'
                        }),
                        'meta_description': forms.Textarea(attrs={
                            'class': 'form-control',
                            'rows': 3,
                            'placeholder': '留空则使用文章摘要'
                        }),
                        'meta_keywords': forms.TextInput(attrs={
                            'class': 'form-control',
                            'placeholder': '用逗号分隔关键词'
                        })
                    }
                    labels = {
                        'title': '文章标题',
                        'slug': 'URL别名',
                        'content': '文章内容',
                        'category': '文章分类',
                        'featured_image': '特色图片',
                        'status': '发布状态',
                        'is_featured': '精选文章',
                        'meta_title': 'SEO标题',
                        'meta_description': 'SEO描述',
                        'meta_keywords': 'SEO关键词'
                    }
                    help_texts = {
                        'slug': '将用于生成文章URL,建议使用英文字母、数字和连字符',
                        'content': '支持Markdown语法,可以使用代码高亮、表格等功能',
                        'featured_image': '建议尺寸:1200x630像素,支持jpg、png格式',
                        'is_featured': '精选文章将在首页特别展示',
                        'meta_keywords': '多个关键词请用英文逗号分隔'
                    }

                def __init__(self, *args, **kwargs):
                    self.user = kwargs.pop('user', None)
                    super().__init__(*args, **kwargs)

                    # 根据用户权限调整表单
                    if self.user and not self.user.has_perm('blog.can_publish_post'):
                        # 普通用户不能直接发布文章
                        self.fields['status'].choices = [
                            ('draft', '草稿'),
                        ]
                        self.fields['status'].initial = 'draft'
                        self.fields['status'].widget.attrs['disabled'] = 'disabled'

                    if self.user and not self.user.has_perm('blog.can_feature_post'):
                        # 普通用户不能设置精选
                        self.fields['is_featured'].widget.attrs['disabled'] = 'disabled'

                    # 动态设置分类选项
                    if self.user and not self.user.is_staff:
                        # 非管理员只能选择自己的分类
                        self.fields['category'].queryset = Category.objects.filter(
                            allowed_users=self.user
                        )

                    # 如果是编辑模式,调整标签显示
                    if self.instance and self.instance.pk:
                        self.fields['tags'].initial = self.instance.tags.all()

                def clean_slug(self):
                    """验证URL别名唯一性"""
                    slug = self.cleaned_data.get('slug')
                    title = self.cleaned_data.get('title')

                    # 如果没有填写slug,根据标题生成
                    if not slug and title:
                        from django.utils.text import slugify
                        slug = slugify(title)

                    # 验证唯一性(编辑时排除当前文章)
                    queryset = BlogPost.objects.filter(slug=slug)
                    if self.instance and self.instance.pk:
                        queryset = queryset.exclude(pk=self.instance.pk)

                    if queryset.exists():
                        raise forms.ValidationError('该URL别名已存在,请使用其他别名')

                    return slug

                def clean_content(self):
                    """验证文章内容"""
                    content = self.cleaned_data.get('content')
                    if content and len(content) < 50:
                        raise forms.ValidationError('文章内容不能少于50个字符')
                    return content

                def clean_featured_image(self):
                    """验证特色图片"""
                    image = self.cleaned_data.get('featured_image')
                    if image:
                        # 检查文件大小(最大5MB)
                        if image.size > 5 * 1024 * 1024:
                            raise forms.ValidationError('图片文件大小不能超过5MB')

                        # 检查文件类型
                        if not image.content_type.startswith('image/'):
                            raise forms.ValidationError('请上传有效的图片文件')

                        # 检查图片尺寸
                        from PIL import Image
                        try:
                            img = Image.open(image)
                            width, height = img.size
                            if width < 800 or height < 400:
                                raise forms.ValidationError(
                                    '图片尺寸太小,建议最小尺寸800x400像素'
                                )
                        except Exception:
                            raise forms.ValidationError('无法读取图片文件,请确保文件格式正确')

                    return image

                def clean(self):
                    """表单整体验证"""
                    cleaned_data = super().clean()
                    status = cleaned_data.get('status')
                    publish_later = cleaned_data.get('publish_later')
                    publish_date = cleaned_data.get('publish_date')
                    custom_excerpt = cleaned_data.get('custom_excerpt')

                    # 检查发布时间
                    if publish_later and not publish_date:
                        self.add_error(
                            'publish_date',
                            '选择稍后发布时必须指定发布时间'
                        )

                    if publish_date and publish_date <= timezone.now():
                        self.add_error(
                            'publish_date',
                            '发布时间必须是未来时间'
                        )

                    # 设置SEO字段默认值
                    title = cleaned_data.get('title')
                    if title and not cleaned_data.get('meta_title'):
                        cleaned_data['meta_title'] = title

                    content = cleaned_data.get('content')
                    if custom_excerpt:
                        cleaned_data['excerpt'] = custom_excerpt
                    elif content and not cleaned_data.get('excerpt'):
                        # 自动生成摘要
                        cleaned_data['excerpt'] = content[:200] + '...' if len(content) > 200 else content

                    return cleaned_data

                def save(self, commit=True):
                    """保存表单数据"""
                    post = super().save(commit=False)

                    # 设置作者
                    if not post.author and self.user:
                        post.author = self.user

                    # 处理发布时间
                    status = self.cleaned_data.get('status')
                    publish_later = self.cleaned_data.get('publish_later')
                    publish_date = self.cleaned_data.get('publish_date')

                    if status == 'published':
                        if publish_later and publish_date:
                            post.published_at = publish_date
                        elif not post.published_at:
                            post.published_at = timezone.now()
                    else:
                        # 非发布状态清空发布时间
                        post.published_at = None

                    if commit:
                        post.save()
                        # 保存多对多关系
                        self.save_m2m()

                    return post

            # 视图中使用ModelForm
            class PostCreateView(LoginRequiredMixin, CreateView):
                model = BlogPost
                form_class = BlogPostForm
                template_name = 'blog/post_create.html'
                success_url = reverse_lazy('blog:post_list')

                def get_form_kwargs(self):
                    kwargs = super().get_form_kwargs()
                    kwargs['user'] = self.request.user
                    return kwargs

                def form_valid(self, form):
                    form.instance.author = self.request.user
                    messages.success(self.request, '文章创建成功!')
                    return super().form_valid(self)

                def form_invalid(self, form):
                    messages.error(self.request, '请检查表单中的错误')
                    return super().form_invalid(form)
            ---
    c.自定义表单字段
        a.功能说明
            该示例展示了核心功能的实现方法,包含必要的配置和代码逻辑,可直接应用于实际项目开发。
        b.代码示例
            ---
            # forms.py
            from django import forms
            from django.core.exceptions import ValidationError
            from django.utils.translation import gettext_lazy as _
            import re

            class PhoneField(forms.CharField):
                widget = forms.TextInput(attrs={
                    'class': 'form-control',
                    'placeholder': '请输入手机号码'
                })

                def __init__(self, *args, **kwargs):
                    kwargs.setdefault('max_length', 20)
                    super().__init__(*args, **kwargs)

                def __init__(self, *args, **kwargs):
                    choices = kwargs.pop('choices', [
                        (1, '★☆☆☆☆'),
                        (2, '★★☆☆☆'),
                        (3, '★★★☆☆'),
                        (4, '★★★★☆'),
                        (5, '★★★★★'),
                    ])
                    kwargs['choices'] = choices
                    super().__init__(*args, **kwargs)

                def validate(self, value):
                    super().validate(value)
                    if value:
                        try:
                            rating = int(value)
                            if rating < 1 or rating > 5:
                                raise ValidationError(
                                    _('评分必须在1到5之间'),
                                    code='invalid_rating'
                                )
                        except ValueError:
                            raise ValidationError(
                                _('请输入有效的评分'),
                                code='invalid_rating_format'
                            )

            class TagInputField(forms.CharField):
                """标签输入字段"""
                widget = forms.TextInput(attrs={
                    'class': 'form-control tag-input',
                    'placeholder': '输入标签后按回车添加',
                    'data-role': 'tagsinput'
                })

                def __init__(self, *args, **kwargs):
                    self.tag_model = kwargs.pop('tag_model', None)
                    super().__init__(*args, **kwargs)

                def to_python(self, value):
                    if not value:
                        return []

                    # 分割标签
                    tags = [tag.strip() for tag in value.split(',') if tag.strip()]
                    return tags

                def validate(self, value):
                    super().validate(value)

                    if value:
                        for tag in value:
                            if len(tag) > 20:
                                raise ValidationError(
                                    _('标签 "%(tag)s" 长度不能超过20个字符') % {'tag': tag},
                                    code='tag_too_long'
                                )

                            # 验证标签是否包含特殊字符
                            if re.search(r'[^\w\u4e00-\u9fff\-_\s]', tag):
                                raise ValidationError(
                                    _('标签 "%(tag)s" 包含无效字符') % {'tag': tag},
                                    code='invalid_tag_chars'
                                )

                def clean(self, value):
                    tags = super().clean(value)
                    if tags:
                        # 去重并排序
                        tags = list(set(tag.lower() for tag in tags))
                        tags.sort()
                    return tags

            # 使用自定义字段的表单
            class UserProfileForm(forms.ModelForm):
                phone = PhoneField(
                    label='手机号码',
                    required=False
                )

                favorite_color = ColorField(
                    label='喜欢的颜色',
                    required=False
                )

                contact_emails = MultiEmailField(
                    label='联系邮箱',
                    required=False,
                    help_text='每行输入一个邮箱地址'
                )

                rating = RatingField(
                    label='评分',
                    required=False
                )

                interests = TagInputField(
                    label='兴趣爱好',
                    required=False,
                    help_text='输入多个兴趣爱好,用逗号分隔'
                )

                class Meta:
                    model = UserProfile
                    fields = ['bio', 'avatar', 'phone', 'favorite_color', 'contact_emails', 'rating', 'interests']
                    widgets = {
                        'bio': forms.Textarea(attrs={
                            'class': 'form-control',
                            'rows': 4
                        }),
                        'avatar': forms.FileInput(attrs={
                            'class': 'form-control-file'
                        })
                    }
                    labels = {
                        'bio': '个人简介',
                        'avatar': '头像'
                    }

            class AdvancedSearchForm(forms.Form):
                """高级搜索表单,使用多种自定义字段"""
                keyword = forms.CharField(
                    label='关键词',
                    widget=forms.TextInput(attrs={
                        'class': 'form-control',
                        'placeholder': '搜索关键词'
                    }),
                    required=False
                )

                category = forms.ModelChoiceField(
                    queryset=Category.objects.all(),
                    widget=forms.Select(attrs={'class': 'form-control'}),
                    empty_label='所有分类',
                    required=False
                )

                tags = TagInputField(
                    label='标签',
                    required=False
                )

                date_from = forms.DateField(
                    label='开始日期',
                    widget=forms.DateInput(attrs={
                        'class': 'form-control',
                        'type': 'date'
                    }),
                    required=False
                )

                date_to = forms.DateField(
                    label='结束日期',
                    widget=forms.DateInput(attrs={
                        'class': 'form-control',
                        'type': 'date'
                    }),
                    required=False
                )

                author = forms.CharField(
                    label='作者',
                    widget=forms.TextInput(attrs={
                        'class': 'form-control',
                        'placeholder': '输入作者用户名'
                    }),
                    required=False
                )

                min_rating = RatingField(
                    label='最低评分',
                    required=False
                )

                sort_by = forms.ChoiceField(
                    label='排序方式',
                    choices=[
                        ('relevance', '相关性'),
                        ('date_desc', '最新发布'),
                        ('date_asc', '最早发布'),
                        ('title_asc', '标题 A-Z'),
                        ('title_desc', '标题 Z-A'),
                        ('rating_desc', '评分最高'),
                        ('views_desc', '浏览最多'),
                    ],
                    initial='relevance',
                    widget=forms.Select(attrs={'class': 'form-control'})
                )

                def clean(self):
                    cleaned_data = super().clean()
                    date_from = cleaned_data.get('date_from')
                    date_to = cleaned_data.get('date_to')

                    if date_from and date_to and date_from > date_to:
                        raise ValidationError('开始日期不能晚于结束日期')

                    return cleaned_data
            ---

02.表单验证和清理
    a.字段级验证
        a.功能说明
            该示例展示了核心功能的实现方法,包含必要的配置和代码逻辑,可直接应用于实际项目开发。
        b.代码示例
            ---
            # forms.py
            from django import forms
            from django.core.exceptions import ValidationError
            from django.utils.translation import gettext_lazy as _
            from django.core.validators import RegexValidator
            import re
            from datetime import date, datetime

            class RegistrationForm(forms.Form):
                username = forms.CharField(
                    label=_('用户名'),
                    max_length=150,
                    widget=forms.TextInput(attrs={
                        'class': 'form-control',
                        'placeholder': _('4-20个字符,支持字母、数字和下划线')
                    }),
                    validators=[
                        RegexValidator(
                            regex=r'^\d{17}[\dXx]$',
                            message=_('请输入有效的18位身份证号')
                        )
                    ]
                )

                agreement = forms.BooleanField(
                    label=_('我已阅读并同意服务条款和隐私政策'),
                    widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
                )

                # 验证码
                captcha = forms.CharField(
                    label=_('验证码'),
                    widget=forms.TextInput(attrs={
                        'class': 'form-control',
                        'placeholder': _('请输入验证码')
                    })
                )

                def clean_username(self):
                    """用户名验证"""
                    username = self.cleaned_data.get('username')

                    # 长度检查
                    if len(username) < 4:
                        raise ValidationError(_('用户名长度不能少于4个字符'))

                    # 非法字符检查
                    if re.search(r'[^a-zA-Z0-9_]', username):
                        raise ValidationError(_('用户名只能包含字母、数字和下划线'))

                    # 特殊用户名检查
                    forbidden_usernames = ['admin', 'root', 'system', 'test', 'guest']
                    if username.lower() in forbidden_usernames:
                        raise ValidationError(_('该用户名已被系统保留'))

                    # 重复性检查
                    from django.contrib.auth.models import User
                    if User.objects.filter(username__iexact=username).exists():
                        raise ValidationError(_('该用户名已被使用'))

                    return username.lower()

                def clean_email(self):
                    """邮箱验证"""
                    email = self.cleaned_data.get('email')

                    # 邮箱格式检查
                    if not re.match(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', email):
                        raise ValidationError(_('请输入有效的邮箱地址'))

                    # 禁用邮箱域名检查
                    forbidden_domains = ['10minutemail.com', 'tempmail.org', 'mailinator.com']
                    domain = email.split('@')[1].lower()
                    if domain in forbidden_domains:
                        raise ValidationError(_('请使用真实的邮箱地址,不要使用临时邮箱'))

                    # 重复性检查
                    from django.contrib.auth.models import User
                    if User.objects.filter(email__iexact=email).exists():
                        raise ValidationError(_('该邮箱地址已被注册'))

                    return email.lower()

                def clean_password(self):
                    """密码验证"""
                    password = self.cleaned_data.get('password')

                    # 长度检查
                    if len(password) < 8:
                        raise ValidationError(_('密码长度不能少于8个字符'))

                    # 复杂度检查
                    if not re.search(r'[a-zA-Z]', password):
                        raise ValidationError(_('密码必须包含至少一个字母'))

                    if not re.search(r'\d', password):
                        raise ValidationError(_('密码必须包含至少一个数字'))

                    if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
                        raise ValidationError(_('密码必须包含至少一个特殊字符'))

                    # 常见密码检查
                    common_passwords = ['password', '12345678', 'qwerty123', 'admin123']
                    if password.lower() in common_passwords:
                        raise ValidationError(_('请使用更安全的密码,不要使用常见密码组合'))

                    # 检查是否包含用户名
                    username = self.cleaned_data.get('username', '')
                    if username.lower() in password.lower():
                        raise ValidationError(_('密码不能包含用户名'))

                    return password

                def clean_confirm_password(self):
                    """确认密码验证"""
                    confirm_password = self.cleaned_data.get('confirm_password')
                    password = self.cleaned_data.get('password')

                    if confirm_password != password:
                        raise ValidationError(_('两次输入的密码不一致'))

                    return confirm_password

                def clean_birth_date(self):
                    """出生日期验证"""
                    birth_date = self.cleaned_data.get('birth_date')

                    if birth_date:
                        today = date.today()
                        age = today.year - birth_date.year - (
                            (today.month, today.day) < (birth_date.month, birth_date.day)
                        )

                        if age < 13:
                            raise ValidationError(_('您必须年满13岁才能注册'))

                        if age > 120:
                            raise ValidationError(_('请输入有效的出生日期'))

                    return birth_date

                def clean_phone(self):
                    """手机号码验证"""
                    phone = self.cleaned_data.get('phone')

                    if phone:
                        # 移除所有非数字字符
                        phone_digits = re.sub(r'[^\d+]', '', phone)

                        # 中国手机号验证
                        if not re.match(r'^(\+86)?1[3-9]\d{9}$', phone_digits):
                            raise ValidationError(_('请输入有效的中国手机号码'))

                        return phone_digits

                    return phone

                def clean_id_number(self):
                    """身份证号验证"""
                    id_number = self.cleaned_data.get('id_number')

                    if id_number:
                        id_number = id_number.upper()

                        # 基本格式检查
                        if not re.match(r'^\d{17}[\dX]$', id_number):
                            raise ValidationError(_('请输入有效的18位身份证号'))

                        # 地区码验证(简单检查)
                        area_code = id_number[:6]
                        if not (110000 <= int(area_code) <= 659004):
                            raise ValidationError(_('身份证号地区码无效'))

                        # 出生日期验证
                        try:
                            birth_date = datetime.strptime(id_number[6:14], '%Y%m%d').date()
                        except ValueError:
                            raise ValidationError(_('身份证号出生日期无效'))

                        # 年龄检查
                        today = date.today()
                        age = today.year - birth_date.year - (
                            (today.month, today.day) < (birth_date.month, birth_date.day)
                        )

                        if age < 13 or age > 120:
                            raise ValidationError(_('身份证号年龄不符合要求'))

                        # 校验码验证
                        weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
                        check_codes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']

                        total = 0
                        for i in range(17):
                            total += int(id_number[i]) * weights[i]

                        check_code = check_codes[total % 11]
                        if id_number[-1] != check_code:
                            raise ValidationError(_('身份证号校验码无效'))

                        return id_number

                    return id_number

                def clean_captcha(self):
                    """验证码验证"""
                    captcha = self.cleaned_data.get('captcha')
                    stored_captcha = self.request.session.get('captcha', '')

                    if not captcha or captcha.upper() != stored_captcha.upper():
                        raise ValidationError(_('验证码错误'))

                    return captcha

                def clean(self):
                    """表单整体验证"""
                    cleaned_data = super().clean()
                    first_name = cleaned_data.get('first_name')
                    last_name = cleaned_data.get('last_name')
                    birth_date = cleaned_data.get('birth_date')
                    id_number = cleaned_data.get('id_number')

                    # 姓名检查
                    if first_name and last_name:
                        full_name = f"{first_name}{last_name}"
                        if len(full_name) < 2 or len(full_name) > 30:
                            raise ValidationError(_('姓名长度应在2-30个字符之间'))

                        # 检查是否包含数字
                        if re.search(r'\d', full_name):
                            raise ValidationError(_('姓名不能包含数字'))

                    # 身份证和出生日期一致性检查
                    if birth_date and id_number:
                        try:
                            id_birth_date = datetime.strptime(id_number[6:14], '%Y%m%d').date()
                            if birth_date != id_birth_date:
                                raise ValidationError(_('出生日期与身份证号不匹配'))
                        except ValueError:
                            pass  # 已在上面的方法中处理

                    return cleaned_data
            ---
    b.表单级验证
        a.功能说明
            该示例展示了核心功能的实现方法,包含必要的配置和代码逻辑,可直接应用于实际项目开发。
        b.代码示例
            ---
            # forms.py
            class ProductOrderForm(forms.Form):
                product = forms.ModelChoiceField(
                    queryset=Product.objects.all(),
                    label='选择产品',
                    widget=forms.Select(attrs={'class': 'form-control'})
                )

                quantity = forms.IntegerField(
                    label='数量',
                    min_value=1,
                    widget=forms.NumberInput(attrs={'class': 'form-control'})
                )

                unit_price = forms.DecimalField(
                    label='单价',
                        except AttributeError:
                            pass

                def clean_quantity(self):
                    """数量验证"""
                    quantity = self.cleaned_data.get('quantity')
                    product = self.cleaned_data.get('product')

                    if product and quantity:
                        if quantity > product.stock:
                            raise ValidationError(
                                _('库存不足,当前库存为 %(stock)d 件') % {'stock': product.stock}
                            )

                        if quantity > product.max_purchase_quantity:
                            raise ValidationError(
                                _('单次购买数量不能超过 %(max)d 件') % {'max': product.max_purchase_quantity}
                            )

                    return quantity

                def clean_discount_code(self):
                    """优惠码验证"""
                    discount_code = self.cleaned_data.get('discount_code')

                    if discount_code:
                        try:
                            from .models import DiscountCode
                            code = DiscountCode.objects.filter(
                                code=discount_code.upper(),
                                active=True
                            ).first()

                            if not code:
                                raise ValidationError(_('优惠码无效或已过期'))

                            if code.usage_limit and code.used_count >= code.usage_limit:
                                raise ValidationError(_('优惠码使用次数已达上限'))

                            if code.min_amount:
                                # 暂时无法验证总金额,在clean中处理
                                pass

                            self.discount_code_obj = code

                        except Exception:
                            raise ValidationError(_('优惠码验证失败'))

                    return discount_code

                def clean_contact_phone(self):
                    """联系电话验证"""
                    phone = self.cleaned_data.get('contact_phone')

                    if phone:
                        # 中国手机号验证
                        if not re.match(r'^1[3-9]\d{9}$', phone.replace(' ', '')):
                            raise ValidationError(_('请输入有效的中国手机号码'))

                    return phone

                def clean(self):
                    """表单整体验证"""
                    cleaned_data = super().clean()
                    product = cleaned_data.get('product')
                    quantity = cleaned_data.get('quantity')
                    payment_method = cleaned_data.get('payment_method')
                    shipping_method = cleaned_data.get('shipping_method')
                    discount_code = cleaned_data.get('discount_code')

                    # 计算总金额
                    total_amount = 0
                    if product and quantity:
                        total_amount = product.price * quantity

                        # 应用优惠码
                        if hasattr(self, 'discount_code_obj') and self.discount_code_obj:
                            code = self.discount_code_obj

                            if code.min_amount and total_amount < code.min_amount:
                                raise ValidationError(
                                    _('订单金额需满 %(amount)d 元才能使用此优惠码') % {
                                        'amount': code.min_amount
                                    }
                                )

                            # 计算折扣金额
                            if code.discount_type == 'percentage':
                                discount_amount = total_amount * code.discount_value / 100
                            else:
                                discount_amount = min(code.discount_value, total_amount)

                            total_amount -= discount_amount

                            # 记录折扣信息
                            self.final_discount_amount = discount_amount

                        # 配送费用
                        shipping_fees = {
                            'standard': 10,
                            'express': 20,
                            'overnight': 50
                        }
                        total_amount += shipping_fees.get(shipping_method, 10)

                        self.final_total_amount = total_amount

                    # 支付方式限制检查
                    if payment_method == 'cod' and total_amount > 1000:
                        raise ValidationError(
                            _('货到付款订单金额不能超过1000元,请选择其他支付方式')
                        )

                    # 特殊商品支付方式限制
                    if product and product.payment_method_restricted:
                        allowed_methods = product.allowed_payment_methods.split(',')
                        if payment_method not in allowed_methods:
                            raise ValidationError(
                                _('该商品仅支持 %(methods)s 支付') % {
                                    'methods': '、'.join(
                                        dict(self.fields['payment_method'].choices)[method]
                                        for method in allowed_methods
                                    )
                                }
                            )

                    # 配送方式和地区限制
                    if shipping_method == 'overnight':
                        address = cleaned_data.get('delivery_address', '')
                        if '偏远地区' in address or '新疆' in address or '西藏' in address:
                            raise ValidationError(_('偏远地区不支持隔夜配送'))

                    # 验证用户购买限制
                    if self.user and self.user.is_authenticated and product:
                        from django.utils import timezone
                        from datetime import timedelta

                        # 检查24小时内购买数量限制
                        recent_orders = Order.objects.filter(
                            user=self.user,
                            product=product,
                            created_at__gte=timezone.now() - timedelta(hours=24),
                            status__in=['pending', 'paid', 'shipped']
                        )

                        recent_quantity = sum(order.quantity for order in recent_orders)
                        if recent_quantity + quantity > product.daily_purchase_limit:
                            raise ValidationError(
                                _('24小时内购买此商品数量不能超过 %(limit)d 件,您已购买 %(purchased)d 件') % {
                                    'limit': product.daily_purchase_limit,
                                    'purchased': recent_quantity
                                }
                            )

                    return cleaned_data

            class EventRegistrationForm(forms.Form):
                """活动报名表单"""
                event = forms.ModelChoiceField(
                    queryset=Event.objects.all(),
                    label='选择活动',
                    widget=forms.Select(attrs={'class': 'form-control'})
                )

                participants = forms.IntegerField(
                    label='参与人数',
                    min_value=1,
                    widget=forms.NumberInput(attrs={'class': 'form-control'})
                )

                name = forms.CharField(
                    label='联系人姓名',
                    widget=forms.TextInput(attrs={
                        'class': 'form-control',
                        'placeholder': '请输入真实姓名'
                    })
                )

                email = forms.EmailField(
                    label='邮箱地址',
                    widget=forms.EmailInput(attrs={
                        'class': 'form-control',
                        'placeholder': '用于接收活动通知'
                    })
                )

                phone = forms.CharField(
                    label='联系电话',
                    widget=forms.TextInput(attrs={
                        'class': 'form-control',
                        'placeholder': '请输入手机号码'
                    })
                )

                company = forms.CharField(
                    label='公司/单位',
                    required=False,
                    widget=forms.TextInput(attrs={
                        'class': 'form-control',
                        'placeholder': '请输入公司或单位名称'
                    })
                )

                position = forms.CharField(
                    label='职位',
                    required=False,
                    widget=forms.TextInput(attrs={
                        'class': 'form-control',
                        'placeholder': '请输入职位'
                    })
                )

                dietary_restrictions = forms.CharField(
                    label='饮食限制',
                    required=False,
                    widget=forms.Textarea(attrs={
                        'class': 'form-control',
                        'rows': 2,
                        'placeholder': '如有特殊饮食要求请说明'
                    })
                )

                emergency_contact = forms.CharField(
                    label='紧急联系人',
                    required=False,
                    widget=forms.TextInput(attrs={
                        'class': 'form-control',
                        'placeholder': '紧急联系人姓名'
                    })
                )

                emergency_phone = forms.CharField(
                    label='紧急联系电话',
                    required=False,
                    widget=forms.TextInput(attrs={
                        'class': 'form-control',
                        'placeholder': '紧急联系人电话'
                    })
                )

                agreement = forms.BooleanField(
                    label='我同意活动条款和隐私政策',
                    widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
                )

                def clean_participants(self):
                    """参与人数验证"""
                    participants = self.cleaned_data.get('participants')
                    event = self.cleaned_data.get('event')

                    if event and participants:
                        if participants < 1:
                            raise ValidationError(_('参与人数至少为1人'))

                        if participants > event.max_participants_per_registration:
                            raise ValidationError(
                                _('每次最多报名 %(max)d 人') % {
                                    'max': event.max_participants_per_registration
                                }
                            )

                        if participants > event.remaining_capacity:
                            raise ValidationError(
                                _('剩余名额不足,当前仅可报名 %(remaining)d 人') % {
                                    'remaining': event.remaining_capacity
                                }
                            )

                    return participants

                def clean_email(self):
                    """邮箱验证"""
                    email = self.cleaned_data.get('email')
                    event = self.cleaned_data.get('event')

                    if event and email:
                        # 检查是否已经报名过
                        if EventRegistration.objects.filter(
                            event=event,
                            email__iexact=email,
                            status='confirmed'
                        ).exists():
                            raise ValidationError(_('该邮箱已报名过此活动'))

                    return email

                def clean_phone(self):
                    """手机号验证"""
                    phone = self.cleaned_data.get('phone')

                    if phone:
                        # 手机号格式验证
                        if not re.match(r'^1[3-9]\d{9}$', phone.replace(' ', '')):
                            raise ValidationError(_('请输入有效的中国手机号码'))

                    return phone

                def clean_emergency_phone(self):
                    """紧急联系电话验证"""
                    emergency_phone = self.cleaned_data.get('emergency_phone')

                    if emergency_phone and not re.match(r'^1[3-9]\d{9}$', emergency_phone.replace(' ', '')):
                        raise ValidationError(_('请输入有效的中国手机号码'))

                    return emergency_phone

                def clean(self):
                    """表单整体验证"""
                    cleaned_data = super().clean()
                    event = cleaned_data.get('event')
                    participants = cleaned_data.get('participants')
                    dietary_restrictions = cleaned_data.get('dietary_restrictions')
                    emergency_contact = cleaned_data.get('emergency_contact')
                    emergency_phone = cleaned_data.get('emergency_phone')

                    # 检查活动是否已截止报名
                    if event and event.registration_deadline:
                        from django.utils import timezone
                        if timezone.now() > event.registration_deadline:
                            raise ValidationError(_('该活动报名已截止'))

                    # 检查活动是否已满员
                    if event and event.is_full():
                        raise ValidationError(_('该活动报名人数已满'))

                    # 检查年龄限制
                    if event and event.age_restriction:
                        # 这里需要用户填写出生日期信息,简化处理
                        pass

                    # 检查饮食限制是否需要特殊处理
                    if event and event.provides_meals and dietary_restrictions:
                        # 记录特殊饮食要求
                        pass

                    # 检查紧急联系人信息完整性
                    if emergency_contact and not emergency_phone:
                        self.add_error(
                            'emergency_phone',
                            _('填写紧急联系人时必须提供紧急联系电话')
                        )

                    if emergency_phone and not emergency_contact:
                        self.add_error(
                            'emergency_contact',
                            _('填写紧急联系电话时必须提供紧急联系人姓名')
                        )

                    # 检查报名总人数限制
                    if event and participants:
                        current_registrations = EventRegistration.objects.filter(
                            event=event,
                            status='confirmed'
                        ).aggregate(total_participants=models.Sum('participants'))

                        current_total = current_registrations['total_participants'] or 0

                        if current_total + participants > event.max_participants:
                            raise ValidationError(
                                _('活动报名人数已满,当前已报名 %(current)d 人,最多 %(max)d 人') % {
                                    'current': current_total,
                                    'max': event.max_participants
                                }
                            )

                    return cleaned_data
            ---

6.2 Django认证系统

01.用户认证
    a.用户模型和认证后端
        a.功能说明
            Django认证系统提供用户登录、登出、权限检查等功能,使用authenticate验证凭据、login创建会话、logout清除会话,支持装饰器保护视图。
        b.代码示例
            ---
            # models.py - 自定义用户模型
            from django.contrib.auth.models import AbstractUser, BaseUserManager
            from django.db import models
            from django.utils.translation import gettext_lazy as _
            from django.core.mail import send_mail
            import uuid

            class CustomUserManager(BaseUserManager):
                def create_user(self, email, password=None, **extra_fields):
                    if not email:
                        raise ValueError(_('邮箱地址是必填项'))

                    email = self.normalize_email(email)
                    user = self.model(email=email, **extra_fields)
                    user.set_password(password)
                    user.save(using=self._db)

            from django.contrib import messages
            from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
            from django.utils.encoding import force_bytes, force_str
            from django.template.loader import render_to_string
            from django.contrib.sites.shortcuts import get_current_site
            from .tokens import account_activation_token
            from .forms import LoginForm, RegistrationForm, PasswordResetForm, SetPasswordForm

            @require_http_methods(["GET", "POST"])
            def login_view(request):
                """用户登录视图"""
                if request.user.is_authenticated:
                    return redirect('dashboard')

                if request.method == 'POST':
                    form = LoginForm(request.POST)
                    if form.is_valid():
                        email = form.cleaned_data['email']
                        password = form.cleaned_data['password']
                        remember_me = form.cleaned_data.get('remember_me', False)

                        # 验证用户
                        user = authenticate(request, email=email, password=password)

                        if user is not None:
                            if user.is_active:
                                # 登录用户
                                login(request, user)

                                # 记录登录IP
                                from .utils import get_client_ip
                                user.last_login_ip = get_client_ip(request)
                                user.save(update_fields=['last_login_ip'])

                                # 记录登录日志
                                LoginLog.objects.create(
                                    user=user,
                                    ip_address=get_client_ip(request),
                                    user_agent=request.META.get('HTTP_USER_AGENT', ''),
                                    success=True
                                )

                                messages.success(request, '登录成功!欢迎回来!')

                                # 重定向到登录前的页面或首页
                                next_page = request.GET.get('next')
                                return redirect(next_page or 'dashboard')
                            else:
                                messages.error(request, '您的账户已被禁用,请联系管理员')
                        else:
                            # 记录失败登录
                            LoginLog.objects.create(
                                email=email,
                                ip_address=get_client_ip(request),
                                user_agent=request.META.get('HTTP_USER_AGENT', ''),
                                success=False,
                                reason='认证失败'
                            )

                            messages.error(request, '邮箱或密码错误')
                else:
                    form = LoginForm()

                return render(request, 'auth/login.html', {'form': form})

            def logout_view(request):
                """用户退出视图"""
                if request.method == 'POST':
                    logout(request)
                    messages.info(request, '您已成功退出登录')
                    return redirect('login')

                # GET请求显示确认页面
                return render(request, 'auth/logout_confirm.html')

            @require_http_methods(["GET", "POST"])
            def register_view(request):
                """用户注册视图"""
                if request.user.is_authenticated:
                    return redirect('dashboard')

                if request.method == 'POST':
                    form = RegistrationForm(request.POST)
                    if form.is_valid():
                        # 创建用户
                        user = form.save(commit=False)
                        user.is_active = False  # 需要邮箱验证
                        user.save()

                        # 发送激活邮件
                        current_site = get_current_site(request)
                        mail_subject = '激活您的账户'
                        message = render_to_string('auth/activation_email.html', {
                            'user': user,
                            'domain': current_site.domain,
                            'uid': urlsafe_base64_encode(force_bytes(user.pk)),
                            'token': account_activation_token.make_token(user),
                        })
                        user.email_user(mail_subject, message)

                        messages.success(
                            request,
                            '注册成功!我们已向您的邮箱发送了激活链接,请查收并激活账户。'
                        )
                        return redirect('registration_success')
                else:
                    form = RegistrationForm()

                return render(request, 'auth/register.html', {'form': form})

            def activate_account(request, uidb64, token):
                """激活账户视图"""
                try:
                    uid = force_str(urlsafe_base64_decode(uidb64))
                    user = CustomUser.objects.get(pk=uid)
                except (TypeError, ValueError, OverflowError, CustomUser.DoesNotExist):
                    user = None

                if user is not None and account_activation_token.check_token(user, token):
                    user.is_active = True
                    user.is_verified = True
                    user.save()

                    # 自动登录用户
                    login(request, user)

                    messages.success(request, '账户激活成功!欢迎加入我们!')
                    return redirect('dashboard')
                else:
                    messages.error(request, '激活链接无效或已过期')
                    return redirect('login')

            @require_http_methods(["GET", "POST"])
            def password_reset_request(request):
                """密码重置请求视图"""
                if request.method == 'POST':
                    form = PasswordResetForm(request.POST)
                    if form.is_valid():
                        email = form.cleaned_data['email']
                        associated_users = CustomUser.objects.filter(email=email)

                        if associated_users.exists():
                            for user in associated_users:
                                # 生成重置链接
                                current_site = get_current_site(request)
                                mail_subject = '重置您的密码'
                                message = render_to_string('auth/password_reset_email.html', {
                                    'user': user,
                                    'domain': current_site.domain,
                                    'uid': urlsafe_base64_encode(force_bytes(user.pk)),
                                    'token': default_token_generator.make_token(user),
                                })

                                user.email_user(mail_subject, message)

                        messages.success(
                            request,
                            '如果该邮箱地址存在,我们已向您发送了密码重置链接。'
                        )
                        return redirect('password_reset_done')
                else:
                    form = PasswordResetForm()

                return render(request, 'auth/password_reset.html', {'form': form})

            def password_reset_confirm(request, uidb64, token):
                """密码重置确认视图"""
                try:
                    uid = force_str(urlsafe_base64_decode(uidb64))
                    user = CustomUser.objects.get(pk=uid)
                except (TypeError, ValueError, OverflowError, CustomUser.DoesNotExist):
                    user = None

                if user is not None and default_token_generator.check_token(user, token):
                    if request.method == 'POST':
                        form = SetPasswordForm(user, request.POST)
                        if form.is_valid():
                            form.save()
                            messages.success(request, '密码重置成功!请使用新密码登录。')
                            return redirect('password_reset_complete')
                    else:
                        form = SetPasswordForm(user)

                    return render(request, 'auth/password_reset_confirm.html', {'form': form})
                else:
                    messages.error(request, '密码重置链接无效或已过期')
                    return redirect('login')

            # settings.py - 认证配置
            AUTH_USER_MODEL = 'users.CustomUser'

            AUTHENTICATION_BACKENDS = [
                'django.contrib.auth.backends.ModelBackend',
                'users.authentication.EmailOrUsernameModelBackend',
            ]

            LOGIN_URL = 'login'
            LOGIN_REDIRECT_URL = 'dashboard'
            LOGOUT_REDIRECT_URL = 'home'

            # 密码验证配置
            AUTH_PASSWORD_VALIDATORS = [
                {
                    'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
                    'OPTIONS': {
                        'min_length': 8,
                    }
                },
                {
                    'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
                },
                {
                    'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
                },
                {
                    'NAME': 'users.validators.PasswordComplexityValidator',
                },
            ]

            # sessions.py - 自定义认证后端
            from django.contrib.auth.backends import BaseBackend
            from django.contrib.auth import get_user_model
            from django.core.exceptions import ObjectDoesNotExist

            User = get_user_model()

            class EmailOrUsernameModelBackend(BaseBackend):
                """支持邮箱或用户名登录的认证后端"""
                def authenticate(self, request, email=None, password=None, **kwargs):
                    try:
                        # 如果邮箱格式,使用邮箱查找
                        if '@' in email:
                            user = User.objects.get(email=email.lower())
                        else:
                            # 否则使用用户名查找
                            user = User.objects.get(username=email.lower())
                    except User.DoesNotExist:
                        return None

                    if user.check_password(password) and self.user_can_authenticate(user):
                        return user
                    return None

                def get_user(self, user_id):
                    try:
                        user = User.objects.get(pk=user_id)
                    except User.DoesNotExist:
                        return None

                    return user if self.user_can_authenticate(user) else None

            class PhoneBackend(BaseBackend):
                """手机号认证后端"""
                def authenticate(self, request, phone=None, password=None, **kwargs):
                    try:
                        user = User.objects.get(phone=phone)
                    except User.DoesNotExist:
                        return None

                    if user.check_password(password) and self.user_can_authenticate(user):
                        return user
                    return None

                def get_user(self, user_id):
                    try:
                        user = User.objects.get(pk=user_id)
                    except User.DoesNotExist:
                        return None

                    return user if self.user_can_authenticate(user) else None
            ---
    b.权限系统
        a.功能说明
            该示例展示了核心功能的实现方法,包含必要的配置和代码逻辑,可直接应用于实际项目开发。
        b.代码示例
            ---
            # models.py - 自定义权限模型
            from django.db import models
            from django.contrib.auth.models import Permission, Group
            from django.contrib.contenttypes.models import ContentType
            from django.contrib.auth.models import AbstractUser

            class ExtendedPermission(models.Model):
                name = models.CharField('权限名称', max_length=100)
                codename = models.CharField('权限代码', max_length=100, unique=True)
                description = models.TextField('权限描述', blank=True)
                content_type = models.ForeignKey(
                    ContentType,
                    on_delete=models.CASCADE,
                    verbose_name='内容类型'
                )
                created_at = models.DateTimeField('创建时间', auto_now_add=True)

            @login_required
            @user_passes_test(lambda u: u.is_staff or u.groups.filter(name='editors').exists())
            def moderate_posts_view(request):
                """内容审核视图 - 需要管理员或编辑者权限"""
                if request.method == 'POST':
                    action = request.POST.get('action')
                    post_id = request.POST.get('post_id')

                    post = get_object_or_404(Post, pk=post_id)

                    if action == 'approve':
                        post.status = 'published'
                        post.published_at = timezone.now()
                        post.save()
                        messages.success(request, '文章已审核通过')
                    elif action == 'reject':
                        post.status = 'rejected'
                        post.save()
                        messages.success(request, '文章已拒绝')

                    return redirect('moderate_posts')

                # 获取待审核文章
                posts = Post.objects.filter(status='pending').order_by('-created_at')
                return render(request, 'blog/moderate_posts.html', {'posts': posts})

            # 基于类的视图权限检查
            class PostCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
                """创建文章类视图"""
                model = Post
                form_class = PostForm
                template_name = 'blog/post_form.html'
                permission_required = 'blog.add_post'
                raise_exception = True  # 无权限时抛出异常而不是重定向

                def form_valid(self, form):
                    form.instance.author = self.request.user
                    return super().form_valid(form)

            class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
                """更新文章类视图"""
                model = Post
                form_class = PostForm
                template_name = 'blog/post_form.html'

                def test_func(self):
                    """测试用户权限"""
                    post = self.get_object()
                    return (
                        post.author == self.request.user or
                        self.request.user.has_perm('blog.change_post') or
                        self.request.user.has_perm('blog.can_edit_any_post')
                    )

                def handle_no_permission(self):
                    """处理权限不足"""
                    if self.request.user.is_authenticated:
                        return HttpResponseForbidden("您没有权限编辑此文章")
                    return redirect('login')

            # 自定义权限检查装饰器
            def role_required(role_name):
                """角色权限装饰器"""
                def decorator(view_func):
                    def wrapper(request, *args, **kwargs):
                        if not request.user.is_authenticated:
                            return redirect('login')

                        try:
                            user_role = UserRole.objects.get(
                                user=request.user,
                                role__name=role_name,
                                is_active=True
                            )

                            # 检查角色是否过期
                            if user_role.expires_at and user_role.expires_at < timezone.now():
                                user_role.is_active = False
                                user_role.save()
                                return HttpResponseForbidden("您的角色已过期")

                            return view_func(request, *args, **kwargs)

                        except UserRole.DoesNotExist:
                            return HttpResponseForbidden(f"您需要 {role_name} 角色权限")

                    return wrapper
                return decorator

            def has_permission_or_role(permission=None, role=None):
                """权限或角色检查装饰器"""
                def decorator(view_func):
                    def wrapper(request, *args, **kwargs):
                        if not request.user.is_authenticated:
                            return redirect('login')

                        # 检查权限
                        if permission and request.user.has_perm(permission):
                            return view_func(request, *args, **kwargs)

                        # 检查角色
                        if role:
                            try:
                                UserRole.objects.get(
                                    user=request.user,
                                    role__name=role,
                                    is_active=True
                                )
                                return view_func(request, *args, **kwargs)
                            except UserRole.DoesNotExist:
                                pass

                        return HttpResponseForbidden("权限不足")

                    return wrapper
                return decorator

            # 使用自定义装饰器
            @login_required
            @role_required('content_manager')
            def manage_content_view(request):
                """内容管理视图 - 需要内容管理员角色"""
                return render(request, 'admin/manage_content.html')

            @login_required
            @has_permission_or_role(permission='blog.moderate_post', role='moderator')
            def moderate_comments_view(request):
                """评论审核视图 - 需要审核权限或角色"""
                return render(request, 'admin/moderate_comments.html')

            # 权限管理工具
            class PermissionManager:
                """权限管理器"""

                @staticmethod
                def assign_permission(user, permission_codename, content_type=None):
                    """为用户分配权限"""
                    try:
                        if content_type:
                            permission = Permission.objects.get(
                                codename=permission_codename,
                                content_type=content_type
                            )
                        else:
                            permission = Permission.objects.get(codename=permission_codename)

                        user.user_permissions.add(permission)
                        return True
                    except Permission.DoesNotExist:
                        return False

                @staticmethod
                def assign_role(user, role_name, assigned_by=None, expires_at=None):
                    """为用户分配角色"""
                    try:
                        role = Role.objects.get(name=role_name, is_active=True)
                        UserRole.objects.update_or_create(
                            user=user,
                            role=role,
                            defaults={
                                'assigned_by': assigned_by,
                                'expires_at': expires_at,
                                'is_active': True
                            }
                        )
                        return True
                    except Role.DoesNotExist:
                        return False

                @staticmethod
                def revoke_role(user, role_name):
                    """撤销用户角色"""
                    try:
                        role = Role.objects.get(name=role_name)
                        user_role = UserRole.objects.get(user=user, role=role)
                        user_role.is_active = False
                        user_role.save()
                        return True
                    except (Role.DoesNotExist, UserRole.DoesNotExist):
                        return False

                @staticmethod
                def user_has_permission(user, permission_codename):
                    """检查用户是否有指定权限"""
                    return user.has_perm(permission_codename)

                @staticmethod
                def user_has_role(user, role_name):
                    """检查用户是否有指定角色"""
                    try:
                        UserRole.objects.get(
                            user=user,
                            role__name=role_name,
                            is_active=True
                        )
                        return True
                    except UserRole.DoesNotExist:
                        return False

            # 在信号中自动分配权限
            from django.db.models.signals import post_save
            from django.dispatch import receiver

            @receiver(post_save, sender=CustomUser)
            def assign_default_permissions(sender, instance, created, **kwargs):
                """为新用户分配默认权限"""
                if created:
                    # 分组处理
                    if instance.is_staff:
                        # 管理员组
                        try:
                            admin_group = Group.objects.get(name='administrators')
                            instance.groups.add(admin_group)
                        except Group.DoesNotExist:
                            pass
                    else:
                        # 普通用户组
                        try:
                            user_group = Group.objects.get(name='regular_users')
                            instance.groups.add(user_group)
                        except Group.DoesNotExist:
                            pass

                    # 分配个人权限
                    PermissionManager.assign_permission(
                        instance,
                        'blog.add_post'
                    )
                    PermissionManager.assign_permission(
                        instance,
                        'blog.change_post'
                    )
                    PermissionManager.assign_permission(
                        instance,
                        'blog.delete_post'
                    )
            ---
    c.会话管理
        a.功能说明
            会话管理通过request.session字典接口存取数据,支持设置过期时间、清除会话、修改会话键,提供安全的服务器端数据存储机制。
        b.代码示例
            ---
            # settings.py - 会话配置
            SESSION_ENGINE = 'django.contrib.sessions.backends.db'  # 数据库存储
            # SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 缓存存储

            # 会话Cookie设置
            SESSION_COOKIE_NAME = 'sessionid'  # Cookie名称
            SESSION_COOKIE_AGE = 1209600  # Cookie有效期(2周)
            SESSION_COOKIE_DOMAIN = None  # Cookie域名
            SESSION_COOKIE_PATH = '/'  # Cookie路径
            SESSION_COOKIE_SECURE = False  # HTTPS传输
            SESSION_COOKIE_HTTPONLY = True  # 仅HTTP访问
            SESSION_COOKIE_SAMESITE = 'Lax'  # SameSite策略

            # 会话保存设置
            SESSION_SAVE_EVERY_REQUEST = False  # 每次请求都保存会话
            SESSION_EXPIRE_AT_BROWSER_CLOSE = False  # 浏览器关闭时过期

                    product_id = request.POST.get('product_id')
                    quantity = int(request.POST.get('quantity', 1))

                    if action == 'add':
                        # 添加商品到购物车
                        if product_id in cart:
                            cart[product_id]['quantity'] += quantity
                        else:
                            # 获取商品信息
                            try:
                                product = Product.objects.get(pk=product_id)
                                cart[product_id] = {
                                    'name': product.name,
                                    'price': str(product.price),
                                    'quantity': quantity,
                                    'added_at': timezone.now().isoformat()
                                }
                            except Product.DoesNotExist:
                                pass

                    elif action == 'update':
                        # 更新商品数量
                        if product_id in cart:
                            cart[product_id]['quantity'] = quantity

                    elif action == 'remove':
                        # 移除商品
                        if product_id in cart:
                            del cart[product_id]

                    elif action == 'clear':
                        # 清空购物车
                        cart = {}

                    # 保存购物车到会话
                    request.session[cart_key] = cart

                    # 标记会话为已修改
                    request.session.modified = True

                return JsonResponse({'cart': cart, 'cart_count': len(cart)})

            # 自定义会话存储后端
            from django.contrib.sessions.backends.base import SessionBase
            from django.core.cache import caches
            import pickle
            import json

            class CustomSessionStore(SessionBase):
                """自定义会话存储"""

                def __init__(self, session_key=None):
                    super().__init__(session_key)
                    self.cache = caches['sessions']

                def load(self):
                    """加载会话数据"""
                    try:
                        session_data = self.cache.get(self.session_key)
                        if session_data is None:
                            self.create()
                            return {}

                        return self.decode(session_data)
                    except Exception:
                        self.create()
                        return {}

                def create(self):
                    """创建新会话"""
                    self.session_key = self._get_new_session_key()
                    self._session_cache = {}
                    self.save()

                def save(self, must_create=False):
                    """保存会话数据"""
                    if self.session_key is None:
                        return self.create()

                    data = self.encode(self._session_cache)
                    if must_create:
                        # 确保会话不存在
                        result = self.cache.add(self.session_key, data, self.get_expiry_age())
                    else:
                        # 更新现有会话
                        result = self.cache.set(self.session_key, data, self.get_expiry_age())

                    if not result:
                        raise CreateError

                def exists(self, session_key):
                    """检查会话是否存在"""
                    return self.cache.has_key(session_key)

                def delete(self, session_key=None):
                    """删除会话"""
                    if session_key is None:
                        session_key = self.session_key
                    self.cache.delete(session_key)

                @classmethod
                def clear_expired(cls):
                    """清理过期会话"""
                    pass  # 缓存后端自动处理过期

            # 会话中间件扩展
            class SessionMiddlewareExtension:
                """会话中间件扩展"""

                def __init__(self, get_response):
                    self.get_response = get_response

                def __call__(self, request):
                    # 处理会话前的逻辑
                    self._before_session_processing(request)

                    response = self.get_response(request)

                    # 处理会话后的逻辑
                    self._after_session_processing(request, response)

                    return response

                def _before_session_processing(self, request):
                    """会话处理前"""
                    # 检查会话是否过期
                    if request.user.is_authenticated:
                        last_activity = request.session.get('last_activity')
                        if last_activity:
                            last_activity_time = datetime.fromisoformat(last_activity)
                            if timezone.now() - last_activity_time > timedelta(hours=24):
                                # 会话超时,强制重新登录
                                from django.contrib.auth import logout
                                logout(request)

                def _after_session_processing(self, request, response):
                    """会话处理后"""
                    # 设置安全标志的Cookie
                    if request.is_secure():
                        response.set_cookie(
                            'session_security',
                            'secure',
                            max_age=SESSION_COOKIE_AGE,
                            secure=True,
                            httponly=True,
                            samesite='Strict'
                        )

            # 会话分析工具
            class SessionAnalyzer:
                """会话分析工具"""

                @staticmethod
                def get_active_sessions(minutes=30):
                    """获取活跃会话"""
                    cutoff_time = timezone.now() - timedelta(minutes=minutes)
                    sessions = Session.objects.filter(
                        expire_date__gte=cutoff_time
                    )
                    return sessions

                @staticmethod
                def get_session_count_by_ip():
                    """按IP统计会话数量"""
                    from django.db.models import Count
                    sessions = Session.objects.annotate(
                        ip_address=Cast('session_data', output_field=TextField())
                    )
                    # 这里需要解析session_data获取IP信息
                    return sessions

                @staticmethod
                def cleanup_expired_sessions():
                    """清理过期会话"""
                    deleted_count = Session.objects.filter(
                        expire_date__lt=timezone.now()
                    ).delete()[0]
                    return deleted_count

                @staticmethod
                def analyze_user_session_patterns(user_id):
                    """分析用户会话模式"""
                    from django.contrib.auth import get_user_model
                    User = get_user_model()

                    try:
                        user = User.objects.get(pk=user_id)
                        # 分析用户访问模式
                        # 这里需要实现具体的分析逻辑
                        return {
                            'user': user,
                            'avg_session_duration': 0,
                            'preferred_pages': [],
                            'activity_times': []
                        }
                    except User.DoesNotExist:
                        return None

            # API会话管理
            from rest_framework.authentication import BaseAuthentication
            from rest_framework.exceptions import AuthenticationFailed

            class APISessionAuthentication(BaseAuthentication):
                """API会话认证"""

                def authenticate(self, request):
                    # 获取会话token
                    session_token = request.headers.get('X-Session-Token')
                    if not session_token:
                        return None

                    try:
                        # 验证会话token
                        session_data = APISession.objects.get(
                            token=session_token,
                            is_active=True,
                            expires_at__gt=timezone.now()
                        )
                        user = session_data.user
                        return (user, session_data)
                    except APISession.DoesNotExist:
                        raise AuthenticationFailed('无效的会话token')

                def authenticate_header(self, request):
                    return 'Session'

            # REST API会话视图
            from rest_framework.decorators import api_view, permission_classes
            from rest_framework.permissions import IsAuthenticated
            from rest_framework.response import Response
            from rest_framework import status

            @api_view(['POST'])
            @permission_classes([IsAuthenticated])
            def create_api_session(request):
                """创建API会话"""
                # 使现有会话失效
                APISession.objects.filter(user=request.user, is_active=True).update(is_active=False)

                # 创建新的API会话
                api_session = APISession.objects.create(
                    user=request.user,
                    token=generate_token(),
                    expires_at=timezone.now() + timedelta(hours=24),
                    user_agent=request.META.get('HTTP_USER_AGENT', ''),
                    ip_address=request.META.get('REMOTE_ADDR')
                )

                return Response({
                    'token': api_session.token,
                    'expires_at': api_session.expires_at
                }, status=status.HTTP_201_CREATED)
            ---

7 实战与部署

7.1 实战案例

01.功能模块
    a.分类1
        用户管理:注册、登录、个人资料
        文章管理:创建、编辑、删除、发布
        分类标签:分类管理、标签系统
        评论系统:评论发表、回复、审核
        搜索功能:全文搜索、分类筛选
        用户交互:点赞、收藏、分享
    b.数据模型设计
        用户模型扩展
        文章模型设计
        评论模型设计
        模型关系定义

02.博客系统架构设计
    a.项目结构
        blog_project/
        ├── manage.py
        ├── requirements.txt
        ├── blog_project/
        │   ├── __init__.py
        │   ├── settings/
        │   │   ├── __init__.py
        │   │   ├── base.py
        │   │   ├── development.py
        │   │   ├── production.py
        │   │   └── testing.py
        │   ├── urls.py
        │   ├── wsgi.py
        │   └── asgi.py
        ├── apps/
        │   ├── __init__.py
        │   ├── users/
        │   │   ├── __init__.py
        │   │   ├── models.py
        │   │   ├── views.py
        │   │   ├── forms.py
        │   │   ├── urls.py
        │   │   ├── admin.py
        │   │   ├── managers.py
        │   │   ├── signals.py
        │   │   └── migrations/
        │   ├── blog/
        │   │   ├── __init__.py
        │   │   ├── models.py
        │   │   ├── views.py
        │   │   ├── forms.py
        │   │   ├── urls.py
        │   │   ├── admin.py
        │   │   ├── managers.py
        │   │   ├── utils.py
        │   │   ├── signals.py
        │   │   └── migrations/
        │   ├── comments/
        │   │   ├── __init__.py
        │   │   ├── models.py
        │   │   ├── views.py
        │   │   ├── forms.py
        │   │   ├── urls.py
        │   │   ├── admin.py
        │   │   └── migrations/
        │   └── core/
        │       ├── __init__.py
        │       ├── models.py
        │       ├── views.py
        │       ├── utils.py
        │       ├── context_processors.py
        │       ├── middleware.py
        │       ├── templatetags/
        │       └── management/
        ├── templates/
        │   ├── base.html
        │   ├── blog/
        │   ├── users/
        │   ├── comments/
        │   └── core/
        ├── static/
        │   ├── css/
        │   ├── js/
        │   ├── images/
        │   └── fonts/
        └── media/
            ├── avatars/
            ├── posts/
            └── uploads/
    b.功能模块设计
        a.用户模块 (users)
           用户注册、登录、退出
           个人资料管理
           密码重置
           用户权限管理
        b.博客模块 (blog)
           文章CRUD操作
           分类管理
           标签系统
           文章搜索
           SEO优化
        c.评论模块 (comments)
           评论发表和管理
           评论回复
           评论审核
           评论点赞
        d.核心模块 (core)
           通用工具函数
           中间件
           上下文处理器
           自定义标签过滤器

03.数据模型示例
    # apps/users/models.py
    from django.contrib.auth.models import AbstractUser
    from django.db import models
    from django.utils.translation import gettext_lazy as _
    from django.urls import reverse

    class User(AbstractUser):
        """扩展用户模型"""
        username = None  # 不使用username字段
        email = models.EmailField(_('邮箱地址'), unique=True)

        # 基本信息
        first_name = models.CharField(_('姓名'), max_length=50)
        last_name = models.CharField(_('姓氏'), max_length=50)
        nickname = models.CharField(_('昵称'), max_length=50, blank=True)
        bio = models.TextField(_('个人简介'), max_length=500, blank=True)
        avatar = models.ImageField(_('头像'), upload_to='avatars/', blank=True)
        website = models.URLField(_('个人网站'), blank=True)

        # 社交媒体
        github = models.CharField(_('GitHub'), max_length=100, blank=True)
        twitter = models.CharField(_('Twitter'), max_length=100, blank=True)
        linkedin = models.CharField(_('LinkedIn'), max_length=100, blank=True)

        # 设置字段
        email_verified = models.BooleanField(_('邮箱已验证'), default=False)
        is_premium = models.BooleanField(_('高级用户'), default=False)
        theme_preference = models.CharField(
            _('主题偏好'),
            max_length=10,
            choices=[
                ('light', '浅色'),
                ('dark', '深色'),
                ('auto', '自动')
            ],
            default='light'
        )

        # 统计字段
        post_count = models.PositiveIntegerField(_('文章数量'), default=0)
        comment_count = models.PositiveIntegerField(_('评论数量'), default=0)
        like_count = models.PositiveIntegerField(_('获赞数量'), default=0)

        # 时间字段
        last_login_ip = models.GenericIPAddressField(_('最后登录IP'), null=True, blank=True)
        created_at = models.DateTimeField(_('创建时间'), auto_now_add=True)
        updated_at = models.DateTimeField(_('更新时间'), auto_now=True)

        USERNAME_FIELD = 'email'
        REQUIRED_FIELDS = ['first_name', 'last_name']

        class Meta:
            verbose_name = _('用户')
            verbose_name_plural = _('用户')
            db_table = 'blog_user'

        def __str__(self):
            return self.get_full_name() or self.email

        def get_absolute_url(self):
            return reverse('users:profile', kwargs={'pk': self.pk})

        def get_full_name(self):
            """获取全名"""
            return f"{self.first_name} {self.last_name}".strip()

        def get_display_name(self):
            """获取显示名称"""
            return self.nickname or self.get_full_name() or self.email

    # apps/blog/models.py
    from django.contrib.auth import get_user_model
    from django.urls import reverse
    from django.utils.text import slugify
    from django.utils import timezone

    User = get_user_model()

    class Category(models.Model):
        """文章分类"""
        name = models.CharField(_('分类名称'), max_length=100, unique=True)
        slug = models.SlugField(_('URL别名'), unique=True)
        description = models.TextField(_('分类描述'), blank=True)
        color = models.CharField(_('颜色'), max_length=7, default='#007bff')
        icon = models.CharField(_('图标'), max_length=50, blank=True)
        parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True)
        order = models.PositiveIntegerField(_('排序'), default=0)
        is_active = models.BooleanField(_('是否激活'), default=True)

        class Meta:
            verbose_name = _('分类')
            verbose_name_plural = _('分类')
            ordering = ['order', 'name']
            db_table = 'blog_category'

        def __str__(self):
            return self.name

        def get_absolute_url(self):
            return reverse('blog:category_posts', kwargs={'slug': self.slug})

        def get_post_count(self):
            """获取分类下的文章数量"""
            return self.posts.filter(status='published').count()

    class Tag(models.Model):
        """文章标签"""
        name = models.CharField(_('标签名称'), max_length=50, unique=True)
        slug = models.SlugField(_('URL别名'), unique=True)
        description = models.TextField(_('标签描述'), blank=True)
        color = models.CharField(_('颜色'), max_length=7, default='#6c757d')

        class Meta:
            verbose_name = _('标签')
            verbose_name_plural = _('标签')
            ordering = ['name']
            db_table = 'blog_tag'

        def __str__(self):
            return self.name

        def get_absolute_url(self):
            return reverse('blog:tag_posts', kwargs={'slug': self.slug})

        def get_post_count(self):
            """获取标签下的文章数量"""
            return self.posts.filter(status='published').count()

    class PostManager(models.Manager):
        """文章管理器"""
        def published(self):
            return self.filter(status='published')

        def featured(self):
            return self.filter(status='published', is_featured=True)

        def popular(self):
            return self.annotate(
                comment_count=models.Count('comments')
            ).filter(status='published').order_by('-comment_count', '-views_count')

        def by_category(self, category):
            return self.filter(category=category, status='published')

        def by_author(self, author):
            return self.filter(author=author, status='published')

    class Post(models.Model):
        """文章模型"""
        STATUS_CHOICES = [
            ('draft', '草稿'),
            ('published', '已发布'),
            ('archived', '已归档'),
            ('private', '私密'),
        ]

        title = models.CharField(_('标题'), max_length=200)
        slug = models.SlugField(_('URL别名'), max_length=200, unique=True)
        content = models.TextField(_('内容'))
        excerpt = models.TextField(_('摘要'), max_length=500, blank=True)

        # 关联字段
        author = models.ForeignKey(
            User,
            on_delete=models.CASCADE,
            related_name='posts',
            verbose_name=_('作者')
        )
        category = models.ForeignKey(
            Category,
            on_delete=models.SET_NULL,
            null=True,
            blank=True,
            related_name='posts',
            verbose_name=_('分类')
        )
        tags = models.ManyToManyField(
            Tag,
            blank=True,
            related_name='posts',
            verbose_name=_('标签')
        )

        # 图片字段
        featured_image = models.ImageField(
            _('特色图片'),
            upload_to='posts/images/',
            blank=True,
            null=True
        )

        # 状态和标志
        status = models.CharField(
            _('状态'),
            max_length=10,
            choices=STATUS_CHOICES,
            default='draft'
        )
        is_featured = models.BooleanField(_('是否精选'), default=False)
        allow_comments = models.BooleanField(_('允许评论'), default=True)
        is_top = models.BooleanField(_('是否置顶'), default=False)

        # SEO字段
        meta_title = models.CharField(_('SEO标题'), max_length=200, blank=True)
        meta_description = models.TextField(_('SEO描述'), max_length=300, blank=True)
        meta_keywords = models.CharField(_('SEO关键词'), max_length=200, blank=True)

        # 统计字段
        views_count = models.PositiveIntegerField(_('浏览次数'), default=0)
        likes_count = models.PositiveIntegerField(_('点赞次数'), default=0)
        comments_count = models.PositiveIntegerField(_('评论次数'), default=0)

        # 时间字段
        created_at = models.DateTimeField(_('创建时间'), auto_now_add=True)
        updated_at = models.DateTimeField(_('更新时间'), auto_now=True)
        published_at = models.DateTimeField(_('发布时间'), null=True, blank=True)

        objects = PostManager()

        class Meta:
            verbose_name = _('文章')
            verbose_name_plural = _('文章')
            ordering = ['-is_top', '-published_at', '-created_at']
            indexes = [
                models.Index(fields=['status', 'published_at']),
                models.Index(fields=['author', 'status']),
                models.Index(fields=['category', 'status']),
            ]

        def __str__(self):
            return self.title

        def save(self, *args, **kwargs):
            # 自动生成slug
            if not self.slug:
                self.slug = slugify(self.title)

            # 自动生成摘要
            if not self.excerpt:
                from django.utils.html import strip_tags
                plain_content = strip_tags(self.content)
                self.excerpt = plain_content[:200] + '...' if len(plain_content) > 200 else plain_content

            # 自动设置发布时间
            if self.status == 'published' and not self.published_at:
                self.published_at = timezone.now()

            # 自动生成SEO字段
            if not self.meta_title:
                self.meta_title = self.title

            if not self.meta_description and self.excerpt:
                self.meta_description = self.excerpt

            super().save(*args, **kwargs)

        def get_absolute_url(self):
            return reverse('blog:post_detail', kwargs={'slug': self.slug})

        def get_next_post(self):
            """获取下一篇文章"""
            return self.__class__.objects.filter(
                status='published',
                published_at__gt=self.published_at
            ).order_by('published_at').first()

        def get_previous_post(self):
            """获取上一篇文章"""
            return self.__class__.objects.filter(
                status='published',
                published_at__lt=self.published_at
            ).order_by('-published_at').first()

        def get_related_posts(self, limit=5):
            """获取相关文章"""
            related_posts = self.__class__.objects.filter(
                status='published'
            ).filter(
                models.Q(category=self.category) |
                models.Q(tags__in=self.tags.all())
            ).exclude(pk=self.pk).distinct()

            return related_posts[:limit]

        def get_reading_time(self):
            """估算阅读时间(分钟)"""
            word_count = len(self.content.split())
            reading_time = max(1, word_count // 200)  # 假设每分钟200字
            return reading_time

    # apps/comments/models.py
    class CommentManager(models.Manager):
        """评论管理器"""
        def approved(self):
            return self.filter(is_approved=True)

        def by_post(self, post):
            return self.filter(post=post, is_approved=True)

    class Comment(models.Model):
        """评论模型"""
        post = models.ForeignKey(
            'blog.Post',
            on_delete=models.CASCADE,
            related_name='comments',
            verbose_name=_('文章')
        )
        author = models.ForeignKey(
            User,
            on_delete=models.CASCADE,
            related_name='comments',
            verbose_name=_('作者')
        )
        parent = models.ForeignKey(
            'self',
            on_delete=models.CASCADE,
            null=True,
            blank=True,
            related_name='replies',
            verbose_name=_('父评论')
        )

        content = models.TextField(_('评论内容'))
        is_approved = models.BooleanField(_('已审核'), default=False)
        likes_count = models.PositiveIntegerField(_('点赞次数'), default=0)

        created_at = models.DateTimeField(_('创建时间'), auto_now_add=True)
        updated_at = models.DateTimeField(_('更新时间'), auto_now=True)

        objects = CommentManager()

        class Meta:
            verbose_name = _('评论')
            verbose_name_plural = _('评论')
            ordering = ['created_at']
            indexes = [
                models.Index(fields=['post', 'is_approved', 'created_at']),
            ]

        def __str__(self):
            return f"{self.author.get_display_name()} on {self.post.title}"

        def get_replies(self):
            """获取回复"""
            return self.replies.filter(is_approved=True).order_by('created_at')

        def is_reply(self):
            """是否为回复"""
            return self.parent is not None

        def get_level(self):
            """获取评论层级"""
            level = 0
            parent = self.parent
            while parent:
                level += 1
                parent = parent.parent
                if level >= 3:  # 限制回复层级
                    break
            return level

04.视图和URL配置
    # apps/blog/views.py
    from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
    from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
    from django.urls import reverse_lazy
    from django.shortcuts import get_object_or_404, redirect
    from django.contrib import messages
    from django.db.models import Q, Count
    from django.core.paginator import Paginator
    from django.http import JsonResponse, HttpResponseBadRequest
    from django.views.decorators.http import require_POST
    from django.utils.decorators import method_decorator

    from .models import Post, Category, Tag
    from .forms import PostForm, PostSearchForm

    class PostListView(ListView):
        """文章列表视图"""
        model = Post
        template_name = 'blog/post_list.html'
        context_object_name = 'posts'
        paginate_by = 10

        def get_queryset(self):
            queryset = Post.objects.published().select_related('author', 'category').prefetch_related('tags')

            # 搜索功能
            search_form = PostSearchForm(self.request.GET)
            if search_form.is_valid():
                query = search_form.cleaned_data.get('q')
                if query:
                    queryset = queryset.filter(
                        Q(title__icontains=query) |
                        Q(content__icontains=query) |
                        Q(excerpt__icontains=query)
                    )

            # 分类过滤
            category_slug = self.kwargs.get('category_slug')
            if category_slug:
                category = get_object_or_404(Category, slug=category_slug)
                queryset = queryset.filter(category=category)

            # 标签过滤
            tag_slug = self.kwargs.get('tag_slug')
            if tag_slug:
                tag = get_object_or_404(Tag, slug=tag_slug)
                queryset = queryset.filter(tags=tag)

            # 作者过滤
            author_id = self.kwargs.get('author_id')
            if author_id:
                queryset = queryset.filter(author_id=author_id)

            # 排序
            sort_by = self.request.GET.get('sort', 'latest')
            if sort_by == 'popular':
                queryset = queryset.annotate(
                    comment_count=Count('comments')
                ).order_by('-comment_count', '-views_count')
            elif sort_by == 'views':
                queryset = queryset.order_by('-views_count', '-published_at')
            else:
                queryset = queryset.order_by('-is_top', '-published_at')

            return queryset

        def get_context_data(self, **kwargs):
            context = super().get_context_data(**kwargs)

            # 搜索表单
            context['search_form'] = PostSearchForm(self.request.GET)

            # 分类信息
            category_slug = self.kwargs.get('category_slug')
            if category_slug:
                context['current_category'] = get_object_or_404(Category, slug=category_slug)

            # 标签信息
            tag_slug = self.kwargs.get('tag_slug')
            if tag_slug:
                context['current_tag'] = get_object_or_404(Tag, slug=tag_slug)

            # 排序选项
            context['current_sort'] = self.request.GET.get('sort', 'latest')

            # 侧边栏数据
            context['recent_posts'] = Post.objects.published()[:5]
            context['popular_posts'] = Post.objects.published().order_by('-views_count')[:5]
            context['categories'] = Category.objects.filter(is_active=True)
            context['tags'] = Tag.objects.all()[:20]

            return context

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

        def get_queryset(self):
            return Post.objects.published().select_related('author', 'category').prefetch_related(
                'tags', 'comments__author'
            )

        def get_object(self, queryset=None):
            obj = super().get_object(queryset)
            # 增加浏览次数
            Post.objects.filter(pk=obj.pk).update(views_count=F('views_count') + 1)
            return obj

        def get_context_data(self, **kwargs):
            context = super().get_context_data(**kwargs)

            post = context['post']

            # 相关文章
            context['related_posts'] = post.get_related_posts()

            # 下一篇文章
            context['next_post'] = post.get_next_post()

            # 上一篇文章
            context['previous_post'] = post.get_previous_post()

            # 评论
            if post.allow_comments:
                context['comments'] = post.comments.approved().order_by('created_at')
                context['comment_count'] = post.comments.approved().count()

            # 用户是否点赞
            if self.request.user.is_authenticated:
                context['is_liked'] = post.likes.filter(
                    user=self.request.user
                ).exists()

            return context

    class PostCreateView(LoginRequiredMixin, CreateView):
        """创建文章视图"""
        model = Post
        form_class = PostForm
        template_name = 'blog/post_form.html'

        def get_form_kwargs(self):
            kwargs = super().get_form_kwargs()
            kwargs['author'] = self.request.user
            return kwargs

        def form_valid(self, form):
            form.instance.author = self.request.user
            response = super().form_valid(form)
            messages.success(self.request, '文章创建成功!')
            return response

        def get_success_url(self):
            if self.object.status == 'published':
                return self.object.get_absolute_url()
            return reverse_lazy('blog:drafts')

    class PostUpdateView(LoginRequiredMixin, UpdateView):
        """更新文章视图"""
        model = Post
        form_class = PostForm
        template_name = 'blog/post_form.html'

        def get_queryset(self):
            return Post.objects.filter(author=self.request.user)

        def get_form_kwargs(self):
            kwargs = super().get_form_kwargs()
            kwargs['author'] = self.request.user
            return kwargs

        def form_valid(self, form):
            response = super().form_valid(form)
            messages.success(self.request, '文章更新成功!')
            return response

    class PostDeleteView(LoginRequiredMixin, DeleteView):
        """删除文章视图"""
        model = Post
        template_name = 'blog/post_confirm_delete.html'
        success_url = reverse_lazy('blog:post_list')

        def get_queryset(self):
            return Post.objects.filter(author=self.request.user)

        def delete(self, request, *args, **kwargs):
            messages.success(request, '文章删除成功!')
            return super().delete(request, *args, **kwargs)

    # API视图
    @method_decorator(require_POST, name='dispatch')
    class PostLikeView(LoginRequiredMixin, View):
        """文章点赞视图"""
        def post(self, request, pk):
            post = get_object_or_404(Post, pk=pk)

            # 切换点赞状态
            like, created = PostLike.objects.get_or_create(
                post=post,
                user=request.user
            )

            if not created:
                like.delete()
                liked = False
                message = '已取消点赞'
            else:
                liked = True
                message = '点赞成功'

            # 更新点赞计数
            post.likes_count = PostLike.objects.filter(post=post).count()
            post.save(update_fields=['likes_count'])

            return JsonResponse({
                'liked': liked,
                'likes_count': post.likes_count,
                'message': message
            })

    @method_decorator(require_POST, name='dispatch')
    class PostBookmarkView(LoginRequiredMixin, View):
        """文章收藏视图"""
        def post(self, request, pk):
            post = get_object_or_404(Post, pk=pk)

            # 切换收藏状态
            bookmark, created = PostBookmark.objects.get_or_create(
                post=post,
                user=request.user
            )

            if not created:
                bookmark.delete()
                bookmarked = False
                message = '已取消收藏'
            else:
                bookmarked = True
                message = '收藏成功'

            return JsonResponse({
                'bookmarked': bookmarked,
                'message': message
            })

    # 搜索视图
    from django.views import View
    from django.http import JsonResponse

    class PostSearchView(View):
        """文章搜索视图"""
        def get(self, request):
            query = request.GET.get('q', '').strip()
            if not query:
                return JsonResponse({'posts': [], 'total': 0})

            posts = Post.objects.published().filter(
                Q(title__icontains=query) |
                Q(content__icontains=query) |
                Q(excerpt__icontains=query)
            ).select_related('author', 'category').prefetch_related('tags')[:20]

            results = []
            for post in posts:
                results.append({
                    'id': post.pk,
                    'title': post.title,
                    'excerpt': post.excerpt,
                    'author': post.author.get_display_name(),
                    'category': post.category.name if post.category else None,
                    'published_at': post.published_at.strftime('%Y-%m-%d'),
                    'url': post.get_absolute_url(),
                    'thumbnail': post.featured_image.url if post.featured_image else None
                })

            return JsonResponse({
                'posts': results,
                'total': len(results),
                'query': query
            })

    # apps/blog/urls.py
    from django.urls import path
    from . import views

    app_name = 'blog'

    urlpatterns = [
        # 文章列表
        path('', views.PostListView.as_view(), name='post_list'),
        path('search/', views.PostListView.as_view(), name='search'),
        path('category/<slug:category_slug>/', views.PostListView.as_view(), name='category_posts'),
        path('tag/<slug:tag_slug>/', views.PostListView.as_view(), name='tag_posts'),
        path('author/<int:author_id>/', views.PostListView.as_view(), name='author_posts'),

        # 文章详情
        path('post/<slug:slug>/', views.PostDetailView.as_view(), name='post_detail'),

        # 文章管理
        path('create/', views.PostCreateView.as_view(), name='post_create'),
        path('edit/<int:pk>/', views.PostUpdateView.as_view(), name='post_edit'),
        path('delete/<int:pk>/', views.PostDeleteView.as_view(), name='post_delete'),
        path('drafts/', views.DraftListView.as_view(), name='drafts'),

        # API
        path('api/<int:pk>/like/', views.PostLikeView.as_view(), name='post_like'),
        path('api/<int:pk>/bookmark/', views.PostBookmarkView.as_view(), name='post_bookmark'),
        path('api/search/', views.PostSearchView.as_view(), name='api_search'),
    ]

7.2 环境搭建

00.项目环境准备
    a.Windows10
        PyCharm 2020.3.3
    b.Centos7.6
        python-3.7.2、django-2.1.7、cookiecutter-django-2.0.13、MySQL-8.0.20、Redis-3.2.1

01.Python-3.7.2、Pip3-19.0.2
    a.安装依赖
        yum -y install wget
        yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel libffi-devel gcc make
        yum -y install python-devel mysql-devel mysql-lib bzip2-devel
    b.下载Python安装包
        cd /usr/local
        wget https://www.python.org/ftp/python/3.7.2/Python-3.7.2.tar.xz
        tar -xvJf Python-3.7.2.tar.xz
        cd /usr/local/Python-3.7.2
        ./configure --prefix=/usr/local/Python-3.7.2/ --enable-optimizations
        make && make install
    c.环境变量
        echo 'export PATH=$PATH:/usr/local/Python-3.7.2/bin' >> /etc/profile && source /etc/profile
    d.创建软链接
        ln -s /usr/local/Python-3.7.2/bin/python3 /usr/bin/python3
        ln -s /usr/local/Python-3.7.2/bin/pip3 /usr/bin/pip3
    e.检查是否安装成功
        python3 -V
        pip3 -V
    f.用update-alternatives来为整个系统更改Python版本
        a.查看系统自带的Python、pip
            whereis python
            whereis pip
        b.设置优先级
            update-alternatives --install /usr/bin/python python /usr/bin/python2 1
            update-alternatives --install /usr/bin/python python /usr/bin/python3 2
        c.列出可用的Python替代版本
            update-alternatives --list
        d.选择可用的Python替代版本
            update-alternatives --config python
        e.测试
            python --version                                                --Python 3.7.2
            python2 --version                                               --Python 2.7.5
            python3 --version                                               --Python 3.7.2
    g.更改默认python为python3后,无法使用yum命令,原因:yum依赖python2
        a.修改第一处地方
            vi /usr/bin/yum -> 将 #!/usr/bin/python 改为 #!/usr/bin/python2
        b.修改第二处地方
            vi /usr/libexec/urlgrabber-ext-down -> 将 #!/usr/bin/python 改为 #!/usr/bin/python2

02.cookiecutter-django-2.0.13模板,创建answer项目
    a.安装Cookiecutter命令
        pip3 install cookiecutter -i https://pypi.python.org/simple
    b.使用cookiecutter-django-2.0.13模板来创建项目
        cd /root/
        cookiecutter https://github.com/pydanny/cookiecutter-django/archive/2.0.13.zip
        ---------------------------------------------------------------
        You've downloaded C:\Users\mysla\.cookiecutters\cookiecutter-django before.
        Is it okay to delete and re-download it? [yes]: no                          --不下载新版本
        Do you want to re-use the existing version? [yes]: yes                      --仍然使用该本地版本
        project_name [My Awesome Project]: answer                                   --project_name
        project_slug [answer]: answer                                               --project_slug
        description [Behold My Awesome Project!]: answer                            --description
        author_name [Daniel Roy Greenfeld]: halavah                                 --author_name
        domain_name [example.com]: halavah.buzz                                     --domain_name
        email [[email protected]]: [email protected]                                --email
        version [0.1.0]:                                                            --version
        Select open_source_license:
        1 - MIT
        2 - BSD
        3 - GPLv3
        4 - Apache Software License 2.0
        5 - Not open source
        Choose from 1, 2, 3, 4, 5 [1]: 5                                            --Not open source
        timezone [UTC]: Asia/Shanghai                                               --timezone
        windows [n]: n                                                              --windows
        use_pycharm [n]: y                                                          --use_pycharm
        use_docker [n]: n                                                           --use_docker
        Select postgresql_version:
        1 - 10.5
        2 - 10.4
        3 - 10.3
        4 - 10.2
        5 - 10.1
        6 - 9.6
        7 - 9.5
        8 - 9.4
        9 - 9.3
        Choose from 1, 2, 3, 4, 5, 6, 7, 8, 9 [1]: 1                                --postgresql_version
        Select js_task_runner:
        1 - None
        2 - Gulp
        Choose from 1, 2 [1]:                                                       --js_task_runner
        custom_bootstrap_compilation [n]:                                           --bootstrap_compilation
        use_compressor [n]: y                                                       --use_compressor
        use_celery [n]: y                                                           --use_celery
        use_mailhog [n]:                                                            --use_mailhog
        use_sentry [n]:                                                             --use_sentry
        use_whitenoise [n]:                                                         --use_whitenoise
        use_heroku [n]:                                                             --use_heroku
        use_travisci [n]:                                                           --use_travisci
        keep_local_envs_in_vcs [y]: n                                               --keep_local_envs_in_vcs
        debug [n]: y                                                                --debug

03.Pipenv虚拟环境
    a.安装pipenv
        python -m pip install --upgrade pip==21.0.1 && pip install pipenv -i https://pypi.python.org/simple
    b.创建虚拟环境(python==3.7.2)
        cd /root/answer && pipenv --python 3.7.2
        Virtualenv location: /root/.local/share/virtualenvs/answer-tFUt2dYh/bin/python
    c.创建项目环境
        a.进入项目文件夹
            [root@bigdata01 ~]# cd /root/answer
            [root@bigdata01 workspace_pycharm]# ll
            total 8
            -rw-r--r--. 1 root root  192 Mar 18 19:39 Pipfile               --替代原来的requirements.txt
            -rw-r--r--. 1 root root 1082 Mar 18 19:34 Pipfile.lock          --根据Pipfile生成的JSON格式依赖文件
        b.修改镜像源,vi Pipfile
            [[source]]                                                      --设置仓库地址
            url = "https://pypi.tuna.tsinghua.edu.cn/simple"
            verify_ssl = true
            name = "pypi"
            [packages]                                                      --生产环境:项目依赖的包
            django = "==2.1.7"
            pytz = "==2018.9"
            [dev-packages]                                                  --开发环境:项目依赖的包
            [requires]
            python_version = "3.7"
        c.安装项目依赖(django==2.1.7、pytz==2018.9)
            pipenv install django==2.1.7 pytz==2018.9                       --生产环境:更新Pipfile.lock中hash值
            pipenv install django==2.1.7 pytz==2018.9 --skip-lock           --生产环境:跳过更新hash值
            pipenv install django==2.1.7 pytz==2018.9 --skip-lock --dev     --开发环境:跳过更新hash值
            pip freeze > requirements.txt
            pip install -r requirements.txt
            pip unistall -r requirements.txt
            pipenv install -r requirements/local.txt -i https://pypi.python.org/simple --skip-lock
            pipenv install                                                  --安装Pipfile中[packages]的包
            pipenv install --dev                                            --安装Pipfile中[dev-packages]的包
            pipenv install --system                                         --使用系统的pip命令而不是虚拟环境命令
            pipenv install --ignore-pipfile                                 --忽略Pipfile直接安装Pipfile.lock
            pipenv install --skip-lock                                      --忽略Pipfile.lock直接安装Pipfile
            pipenv uninstall --all                                          --删除虚拟环境包,不改变Pipfile
            pipenv uninstall --all-dev                                      --删除虚拟环境包,并从Pipfile删除
        d.启动项目(startapp -> DJANGO_APPS、THIRD_PARTY_APPS、LOCAL_APPS -> install、migrate、runserver)
            pipenv run python manage.py makemigrations                      --针对某个字段进行迁移更新
            pipenv run python manage.py migrate                             --针对某个字段进行重新生成
            pipenv run python manage.py startapp news                       --生成应用
            pipenv run python manage.py runserver 0.0.0.0:8000              --启动项目
            pipenv run python manage.py createsuperuser                     --配置admin(halavah、QETU1234)
    d.其他
        a.管理虚拟环境
            pipenv shell                                                    --进入虚拟环境
            pipenv --rm                                                     --删除虚拟环境
            exit                                                            --退出虚拟环境
            python manage.py createsuperuser                                --进入pipenv创建管理
            python manage.py runserver 192.168.2.128:8000                   --进入pipenv启动项目
        b.生成测试覆盖度报告
            pipenv install coverage --skip-lock                             --安装测试工具
            pipenv run coverage run manage.py test -v 2                     --运行测试用例
            pipenv run coverage html                                        --生成测试报告
        c.常见命令汇总
            pipenv [OPTIONS] COMMAND [ARGS]
            OPTIONS:
                --where                                                     --显示项目文件所在路径
                --venv                                                      --显示虚拟环境文件路径
                --py                                                        --显示解释器的所在路径
                --envs                                                      --显示虚拟环境的选项变量
                --rm                                                        --删除当前使用的虚拟环境
                --bare                                                      --最小化输出
                --completion                                                --完整输出
            COMMAND:
                check                                                       --检查安全漏洞
                graph                                                       --显示当前依赖关系图信息
                install                                                     --安全虚拟环境或第三方库
                lock                                                        --锁定生成Pipfile.lock
                open                                                        --在编辑器中查看一个库
                run                                                         --在虚拟环境中运行命令
                shell                                                       --进入虚拟环境
                uninstall                                                   --卸载一个库
                update                                                      --卸载当前包并安装最新版

04.Windows中PyCharm配置远程开发环境
    a.创建新文件夹
        mkdir D:\software_ware\workspace_pycharm\answer -> PyCharm打开answer新文件夹
    b.Centos7远程项目
        a.配置
            a.打开配置
                Tools -> Deployment -> Configuration -> + -> 类型SFTP -> 名称answer
            b.Connection
                SSH Configurations -> 192.168.2.128、root、4023615
                Root Path:/root
                Web server URL:http://192.168.2.128
                Advanced:Send keep alive messages each -> 2 seconds
            c.Mappings
                Local path:D:\software_ware\workspace_pycharm\answer
                Deployment path:/answer
                Web path:http://192.168.2.128
            d.Excluded Paths
                无
        b.选项
            a.打开配置
                Tools -> Deployment -> Options
            b.修改配置
                Exclude items by name:.svn;.cvs;.idea;.DS_Store;.git;.hg;*.hprof;*.pyc,【名称排除项】
                Operations logging:Details,【操作记录】
                Stop operation on the first error:不勾选,【在遇到第一个错误时停止操作】
                Overwrite up-to-date files:勾选,【覆盖最新文件】
                Preserve files timestamps:勾选,【保留文件时间戳】
                Delete target items when source ones do not exist:勾选,【当源项不存在时删除目标项】
                Create empty directories:勾选,【创建空目录】
                Prompt when overwriting or deleting local items:勾选,【覆盖或删除本地项时提示】
                Upload changed files automatically to the default server:勾选,【自动将更改的文件上传默认服务器】
                Skip external changes:勾选,【跳过外部变更】
                Delete remote files when local are deleted:勾选,【本地删除后删除远程文件】
                Override default permissions on files:rwxrwxrwx(777),【重写文件的默认权限】
                Override default permissions on folders:rwxrwxrwx(777),【重写文件夹的默认权限】
                Warn when uploading over newer file:No,【在有更新的文件情况下进行上传时发出警告】
                Show warning dialog on moving on Remote Host:勾选,【在远程主机上移动时显示警告对话框】
                SFTP Advanced Options(IDE level setting):勾选,SFTP高级选项(IDE级别设置)
                Add new host key to known_hosts:Always,将新主机键添加到known_hosts
                Hash hosts in known_hosts file:勾选,known_hosts文件中的哈希主机
    c.Python解释器
        a.选择SSH解释器
            Project: answer -> Python Interpreter -> Add Python Interpreter -> SSH Interpreter
            SSH Configurations -> 192.168.2.128、root、4023615
        b.移动本地IDE编辑器到服务器
            Move
        c.配置SSH解释器对应的服务器相关内容
            Interpreter: /root/.local/share/virtualenvs/answer-tFUt2dYh/bin/python
            Sync folders:D:/software_ware/workspace_pycharm/answer 对应 /root/answer
            Automatically upload project files to the server
    d.同步服务器代码、开启Django支持、项目启动配置
        a.同步代码
            answer -> Deployment -> Sync with Deployed to answer -> 手动Ctrl+S触发自动同步
        b.开启Django支持
            a.开启
                Languages & Frameworks -> Enable Django Support
            b.配置
                Django project root:D:\software_ware\workspace_pycharm\answer
                Settings:config\settings\local.py
                Do not use Diango test runner:不勾选
                Manage script:manage.py
                Environment variables:无
                Folder pattern to track files:无
        c.项目启动配置
            a.Host
                Host:192.168.2.128:8000
                Additional options:无
                Run browser:勾选
                Costom run command:不勾选
                Test server:不勾选
                No reload:不勾选
            b.Environment:
                Environment variables:PYTHONUNBUFFERED=1
                Python interpreter:Interpreter: /root/.local/share/virtualenvs/answer-tFUt2dYh/bin/python
                Interpreter options:无
                Working directory:D:\software_ware\workspace_pycharm\answer
                Path mappings:无
                Add content roots to PYTHONPATH:勾选
                Add source roots to PYTHONPATH:勾选
            c.Logs
                show console when a message is printed to standard output stream:勾选
                show console when a message is printed to standard error stream:勾选

7.3 环境部署

01.部署项目
    a.准备:关闭DEBUG模式,调整静态设置
        vi /www/wwwroot/ProBlog/ProBlog/setting.py
        DEBUG = False                                                       --关闭DEBUG模式
        ALLOWED_HOSTS = ['*']                                               --允许所有人访问
        STATIC_URL = '/static/'
        # STATICFILES_DIRS = [                                              --STATICFILES_DIRS替换STATIC_ROOT
        #     os.path.join(BASE_DIR, 'static'),
        # ]
        STATIC_ROOT = os.path.join(BASE_DIR, "static")                      --STATICFILES_DIRS替换STATIC_ROOT
    b.添加ProBlog项目
        项目名称:ProBlog
        路径:/www/wwwroot/ProBlog
        Python版本:3.7.2
        框架:django
        启动方式:uwsgi
        启动文件/文件夹:/www/wwwroot/ProBlog/ProBlog/wsgi.py
        端口:8000
        是否安装模块依赖:勾选
        开机启动:勾选
    c.修改ProBlog配置(/www/wwwroot/ProBlog/uswgi.ini)
        [uwsgi]
        # 配置和nginx连接的socket连接
        socket = 127.0.0.1:8997
        # 配置项目所在目录
        chdir = /www/wwwroot/ProBlog/
        # 配置wsgi接口模块文件路径,也就是wsgi.py这个文件所在的目录
        wsgi-file = /www/wwwroot/ProBlog/ProBlog/wsgi.py
        # 配置项目静态文件
        static-map = /static=/www/wwwroot/ProBlog/static
        # 配置存放主进程的进程号文件
        pidfile = uwsgi.pid
        # 配置dump日志记录
        daemonize = uwsgi.log
        # 配置启动管理主进程
        master = True
        # 配置启动的进程数
        processes = 4
        # 配置每个进程的线程数
        threads = 2
        # 最大数量的请求
        max-requests = 1000
    d.查看ProBlog虚拟环境,修复静态文件丢失(/www/wwwroot/ProBlog/ProBlog_venv)
        a.进入虚拟环境
            source /www/wwwroot/ProBlog/ProBlog_venv/bin/activate
        b.依赖模块
            python -m pip install --upgrade pip==21.0.1                     --更新pip工具
            pip freeze > requirements.txt                                   --导出requirements.txt
            pip install -r requirements.txt                                 --安装requirements.txt
            pip unistall -r requirements.txt                                --卸载requirements.txt
        c.迁移数据库
            python manage.py makemigrations                                 --迁移数据库
            python manage.py migrate                                        --同步数据库
            python manage.py dumpdata > django_orm.json                     --导出数据
            python manage.py loaddata django_orm.json                       --导入数据
        d.创建管理员、启动项目
            python manage.py createsuperuser                                --进入pipenv创建超级管理员
            python manage.py runserver 192.168.2.128:8000                   --进入pipenv启动项目
        e.修复静态文件丢失
            python manage.py collectstatic                                  --收集静态文件

02.部署网站
    a.添加站点
        域名:www.django.cn
        备注:无
        根目录:/www/wwwroot/ProBlog/
        FTP:不创建
        数据库:不创建
        PHP版本:PHP-56
        网站分类:默认分类
    b.配置文件:新建访问规则
        server
        {
            listen 80;
            server_name www.diango.cn;
            index index.php index.html index.htm default.php default.htm default.html;
            root /www/wwwroot/ProBlog;
            --------------------------------------------------------------------------
            location / {
                include uwsgi_params;
                # 启动端口:同uwsgi.ini的socket连接端口
                uwsgi_pass 127.0.0.1:8997;
                # 启动文件:wsgi.py所在目录名+.wsgi
                uwsgi_param UWSGI_SCRIPT ProBlog.wsgi;
                # 项目路径:ProBlog
                uwsgi_param UWSGI_CHDIR /www/wwwroot/ProBlog/;
            }
            location /static/ {
                # 资源路径:static
                alias /www/wwwroot/ProBlog/static/;
            }
            --------------------------------------------------------------------------
            #SSL-START SSL相关配置,请勿删除或修改下一行带注释的404规则
            #error_page 404/404.html;
            #SSL-END

            #ERROR-PAGE-START  错误页配置,可以注释、删除或修改
            #error_page 404 /404.html;
            #error_page 502 /502.html;
            #ERROR-PAGE-END

            #PHP-INFO-START  PHP引用配置,可以注释或修改
            include enable-php-56.conf;
            #PHP-INFO-END

            #REWRITE-START URL重写规则引用,修改后将导致面板设置的伪静态规则失效
            include /www/server/panel/vhost/rewrite/www.diango.cn.conf;
            #REWRITE-END

            #禁止访问的文件或目录
            location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md)
            {
                return 404;
            }

            #一键申请SSL证书验证目录相关设置
            location ~ \.well-known{
                allow all;
            }

            location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
            {
                expires      30d;
                error_log /dev/null;
                access_log off;
            }

            location ~ .*\.(js|css)?$
            {
                expires      12h;
                error_log /dev/null;
                access_log off;
            }
            access_log  /www/wwwlogs/www.diango.cn.log;
            error_log  /www/wwwlogs/www.diango.cn.error.log;
        }
    c.反向代理:部署静态文件
        location /static/ {
            # 资源路径:static
            root /www/wwwroot/ProBlog/static/;
            break;
        }
        location /media/ {
            # 资源路径:media
            alias /www/wwwroot/ProBlog/media/;
        }

03.运行项目
    a.检查
        是否收集静态文件至static
        DEBUG是否关闭
        宝塔面板-安全里是否放行了8000端口
        云服务器安全组里是否放行了8000端口
        静态文件路径是否有错误,包括在html里的引入要以/static/x x x x开头的绝对路径
    b.运行
        cd /root/ProBlog && pipenv shell
        python manage.py runserver 192.168.2.128:8000
        http://192.168.2.128:8000       daiyi   123456
        http://192.168.2.128:8000/admin daiyi   daiyi123456

7.4 开发项目

01.快速开始
    a.网址
        http://192.168.2.128:8000/accounts/login/
        http://192.168.2.128:8000/accounts/signup/
    b.位置
        /root/.local/share/virtualenvs/answer-tFUt2dYh/bin/python
        /root/.local/share/virtualenvs/zanhu-qCBWC76o/bin/python
    c.密码
        zanhu   zanhu       aFrRZACBBiKJMYdc
        answer  answer      kBeZGFXBEz244mAp