Python开发Django从入门到精通

前言:开启Django的奇幻编程之旅

欢迎来到Django的世界!本书是你的“编程指南”,将带你从零开始,逐步掌握Django的精髓。无论你是编程新手,还是有一定经验的开发者,这本书都将为你提供系统化、专业化的指导。让我们一起踏上这段充满挑战与乐趣的编程旅程吧!


章节目录

第一章:启程!Django魔法学院入学手册

1.1 第一堂咒语课:Python与Django的“前世今生”

  • Python基础速成(专为Django特训)

  • Django的“四象之力”:MTV架构解剖

  • 虚拟环境:别让依赖包打架的结界术

1.2 搭建你的魔法工坊(开发环境)

  • PyCharm vs VS Code:魔杖选哪根?

  • Django安装:一键召唤神龙的仪式

  • 命令行黑魔法:startproject与startapp的真谛

1.3 初探魔法地图:第一个Django项目“Hello Universe”

  • 路由配置:给URL贴上门牌号

  • 视图函数:从“Hello World”到“Hello Universe”

  • 调试模式:当代码炸了,如何优雅甩锅给IDE


第二章:数据炼金术士必修课

2.1 ORM炼金术:将数据模型点石成金

  • Model设计:用类定义你的“数据DNA”

  • 字段类型:字符串、时间、外键的魔法属性

  • 数据库迁移:让模型变身SQL的变形咒

2.2 三神器降临:MySQL × MongoDB × Redis

  • MySQL驯龙记:Django连接、事务与复杂查询优化

  • MongoDB野性召唤:用Djongo征服NoSQL大陆

  • Redis闪电术:缓存、会话与实时排行榜实战

2.3 Admin后台:给非程序员老板的“上帝视角”

  • 定制Admin界面:从毛坯房到精装修

  • 数据批量操作:一键拯救996的摸鱼时刻

  • 权限控制:别让实习生删了生产库!


第三章:让网页跳起踢踏舞,奏响前后端交响曲

3.1 模板引擎:HTML的“乐高积木”哲学

  • 变量与标签:动态内容的灵魂注入

  • 继承与组件:拒绝复制粘贴的重复人生

  • 过滤器:给数据化妆的100种姿势

3.2 视图的千层套路

  • 函数视图 vs 类视图:牛仔与贵族的对决

  • 请求响应循环:Django的“心脏跳动”原理

  • 文件上传与下载:小心内存爆炸的隐藏陷阱

3.3 表单与验证:用户输入的安全结界

  • 表单设计:从零到防机器人攻击

  • CSRF防御:别让黑客偷走你的曲奇(Cookie)

  • 自定义验证器:比如“密码不许用123456”


第四章:进阶!黑魔法防御术

4.1 RESTful API:让Django化身数据快递员

  • DRF(Django REST Framework)入门

  • 序列化器:把模型变成JSON的翻译官

  • JWT认证:没有Session的世界怎么活?

4.2 异步任务:别让用户等到花儿都谢了

  • Celery异步队列:后台任务的隐形斗篷

  • WebSocket实战:用Django Channels实现聊天室

4.3 性能优化:从蜗牛到猎豹的进化论

  • 查询优化:N+1问题的末日审判

  • 缓存策略:Redis、Memcached与CDN的三角恋

  • Gunicorn × Nginx:高并发下的生存法则


第五章:出征!代码的星辰大海

5.1 部署到云端:从本地小白到服务器大神

  • Docker化:把你的应用塞进集装箱

  • AWS/GCP/Aliyun选型指南:哪朵云最懂你的钱包

  • GitHub Actions:自动化部署的机械仆从

5.2 测试与安全:代码的疫苗与防弹衣

  • 单元测试:别等上线才当Debug侠

  • XSS、SQL注入防御:黑客的套路拆解

  • 日志监控:当系统崩了,如何优雅甩锅(第二季)

5.3 从单体到微服务:Django的二次觉醒

  • 微服务架构:拆!拆!拆!

  • GraphQL进阶:想要啥数据自己点

  • 未来趋势:Serverless与Django的量子纠缠


附录: Django魔法师生存工具包

  • 常用命令速查表(migrate? runserver? 拿来吧你!)

  • Django Debug Toolbar:开发者的第三只眼

  • 从开源项目偷师:GitHub宝藏仓库推荐

第一章:启程!Django魔法学院入学手册

欢迎来到 Django魔法学院,在这里,你将踏上一段充满挑战与乐趣的旅程,学习如何利用Python和Django构建强大的Web应用。这本书将带你从入门到精通,逐步揭开Django的神秘面纱,让你掌握这门“魔法”的精髓。

1.1 第一堂咒语课:Python与Django的“前世今生”

  • Python基础速成(专为Django特训)

  • Django的“四象之力”:MTV架构解剖

  • 虚拟环境:别让依赖包打架的结界术

在开始构建你的第一个Django项目之前,我们需要先了解一些基础知识。就像学习任何一门魔法一样,掌握基础咒语是必不可少的。

1.1.1 Python基础速成(专为Django特训)

Python作为Django的“母语”,是你必须熟练掌握的工具。虽然你可能已经对Python有所了解,但我们将专注于那些对Django开发至关重要的概念和技巧。

1.1.1.1 Python 语法速览

Python以其简洁优雅的语法而闻名。以下是一些关键概念:

  • 变量与数据类型: Python是动态类型语言,这意味着你不需要显式声明变量的类型。常见的数据类型包括整数(int)、浮点数(float)、字符串(str)、列表(list)、元组(tuple)和字典(dict)。

    # 示例
    age = 25          # 整数
    height = 5.9      # 浮点数
    name = "Alice"    # 字符串
    fruits = ["apple", "banana", "cherry"]  # 列表
    coordinates = (10.0, 20.0)               # 元组
    person = {"name": "Alice", "age": 25}   # 字典
    
  • 控制结构: 条件语句(if-elif-else)和循环(for, while)是编程的基础。

    # 条件语句
    if age > 18:
        print("成年人")
    elif age == 18:
        print("刚成年")
    else:
        print("未成年人")
    
    # for 循环
    for fruit in fruits:
        print(fruit)
    
    # while 循环
    count = 0
    while count < 5:
        print(count)
        count += 1
    
  • 函数: 函数是组织代码的基本单位。

    def greet(name):
        return f"Hello, {name}!"
    
    print(greet("Alice"))  # 输出: Hello, Alice!
    
  • 类与对象: Python支持面向对象编程,允许你创建类和对象。

    class Person:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def greet(self):
            return f"Hello, my name is {self.name} and I am {self.age} years old."
    
    alice = Person("Alice", 25)
    print(alice.greet())  # 输出: Hello, my name is Alice and I am 25 years old.
    
1.1.1.2 列表推导式与生成器

列表推导式是一种简洁的创建列表的方法,而生成器则提供了一种更节省内存的方式来处理大量数据。

# 列表推导式
squares = [x**2 for x in range(10)]
print(squares)  # 输出: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# 生成器表达式
squares_gen = (x**2 for x in range(10))
for square in squares_gen:
    print(square)
1.1.1.3 装饰器

装饰器是Python中一个强大的功能,允许你以声明的方式修改函数的行为。在Django中,装饰器被广泛用于权限控制、缓存等场景。

def my_decorator(func):
    def wrapper():
        print("Before calling the function")
        func()
        print("After calling the function")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()
# 输出:
# Before calling the function
# Hello!
# After calling the function
1.1.1.4 上下文管理器

上下文管理器用于管理资源,例如文件、网络连接等,确保资源在使用后被正确释放。Django中常用到上下文管理器来处理数据库连接等。

class MyContextManager:
    def __enter__(self):
        print("Entering the context")
        return "Resource"

    def __exit__(self, exc_type, exc_value, traceback):
        print("Exiting the context")

with MyContextManager() as resource:
    print(f"Using {resource}")
# 输出:
# Entering the context
# Using Resource
# Exiting the context

1.1.2 Django的“四象之力”:MTV架构解剖

Django采用了一种独特的架构模式,称为MTV(Model-Template-View)架构,它与传统的MVC(Model-View-Controller)架构类似,但又有其独特之处。

1.1.2.1 模型(Model)

模型是Django应用的核心,负责定义数据结构和与数据库的交互。Django的ORM(对象关系映射)使得你能够使用Python类来定义数据库表和关系。

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=100)
    age = models.IntegerField()
    email = models.EmailField()

    def __str__(self):
        return self.name
1.1.2.2 模板(Template)

模板负责将数据渲染成HTML页面。Django的模板语言提供了强大的功能,例如变量、标签、过滤器等,使得前端开发更加高效。

<!-- templates/person_detail.html -->
<!DOCTYPE html>
<html>
<head>
    <title>{{ person.name }}</title>
</head>
<body>
    <h1>{{ person.name }}</h1>
    <p>Age: {{ person.age }}</p>
    <p>Email: {{ person.email }}</p>
</body>
</html>
1.1.2.3 视图(View)

视图负责处理请求、执行业务逻辑、调用模型获取数据,并将数据传递给模板进行渲染。Django支持两种主要的视图类型:函数视图和类视图。

from django.shortcuts import render, get_object_or_404
from .models import Person

def person_detail(request, person_id):
    person = get_object_or_404(Person, id=person_id)
    return render(request, 'person_detail.html', {'person': person})
1.1.2.4 URL 配置

URL配置负责将URL路径映射到相应的视图函数或类。Django使用URLconf模块来定义URL模式。

from django.urls import path
from . import views

urlpatterns = [
    path('person/<int:person_id>/', views.person_detail, name='person_detail'),
]

1.1.3 虚拟环境:别让依赖包打架的结界术

在开发Python项目时,虚拟环境是一个非常重要的工具。它允许你为每个项目创建独立的Python环境,避免不同项目之间的依赖冲突。

1.1.3.1 为什么要使用虚拟环境?
  • 隔离性: 每个项目都有自己的依赖包,不会相互干扰。
  • 可移植性: 虚拟环境可以与项目代码一起打包,方便部署。
  • 版本控制: 可以为不同的项目使用不同版本的Python或依赖包。
1.1.3.2 使用 venv 创建虚拟环境

Python 3.3+ 版本内置了 venv 模块,可以用来创建虚拟环境。

# 创建虚拟环境
python3 -m venv myenv

# 激活虚拟环境
# Windows
myenv\Scripts\activate

# macOS/Linux
source myenv/bin/activate

# 退出虚拟环境
deactivate
1.1.3.3 使用 pip 管理依赖包

在激活虚拟环境后,可以使用 pip 来安装和管理依赖包。

# 安装 Django
pip install django

# 查看已安装的包
pip list

# 生成依赖清单
pip freeze > requirements.txt

# 安装依赖包
pip install -r requirements.txt

1.2 搭建你的魔法工坊(开发环境)

  • PyCharm vs VS Code:魔杖选哪根?

  • Django安装:一键召唤神龙的仪式

  • 命令行黑魔法:startproject与startapp的真谛

在开始编写Django应用之前,我们需要搭建一个高效的开发环境。选择合适的工具和配置,可以大大提高你的开发效率。

1.2.1 PyCharm vs VS Code:魔杖选哪根?

选择一个合适的代码编辑器或集成开发环境(IDE)是开发过程中至关重要的一步。以下是两种流行的选择:

1.2.1.1 PyCharm
  • 优点:
    • 强大的Django支持: 内置的Django模板支持、代码补全、调试工具等。
    • 丰富的插件: 提供了大量的插件来扩展功能。
    • 集成终端: 内置终端,方便使用命令行工具。
    • 版本控制集成: 集成了Git等版本控制工具。
  • 缺点:
    • 资源消耗较大: 对系统资源要求较高。
    • 付费: 专业版需要付费。
1.2.1.2 VS Code
  • 优点:
    • 轻量级: 资源消耗较小。
    • 免费开源: 完全免费。
    • 丰富的扩展: 拥有庞大的扩展库,支持多种编程语言和工具。
    • 跨平台: 支持Windows、macOS、Linux。
  • 缺点:
    • Django支持不如PyCharm: 需要安装额外的插件来增强Django支持。
1.2.1.3 选择建议
  • 如果你需要强大的Django支持和丰富的功能,并且不介意付费,PyCharm是一个不错的选择。
  • 如果你更注重轻量级和灵活性,并且愿意花一些时间来配置VS Code,它也是一个很好的选择。

1.2.2 Django安装:一键召唤神龙的仪式

安装Django非常简单,只需使用 pip 即可。

pip install django
1.2.2.1 验证安装
python -m django --version

这将输出你安装的Django版本。

1.2.3 命令行黑魔法:startproject 与 startapp 的真谛

Django 提供了两个重要的命令行工具:startproject 和 startapp,用于创建项目和应用程序。

1.2.3.1 创建项目
django-admin startproject myproject

这将在当前目录下创建一个名为 myproject 的Django项目。

项目结构如下:

myproject/
    manage.py
    myproject/
        __init__.py
        settings.py
        urls.py
        wsgi.py
  • manage.py: 一个命令行工具,用于与Django项目进行交互。
  • myproject/: 项目的Python包,包含项目的配置文件。
    • init.py: 标识该目录为一个Python包。
    • settings.py: 项目的配置文件。
    • urls.py: 项目的URL配置。
    • wsgi.py: WSGI应用入口。
1.2.3.2 创建应用
python manage.py startapp myapp

这将在项目中创建一个名为 myapp 的应用。

应用结构如下:

myapp/
    __init__.py
    admin.py
    apps.py
    migrations/
        __init__.py
    models.py
    tests.py
    views.py
  • admin.py: 配置Django admin后台。
  • apps.py: 应用的配置文件。
  • migrations/: 存放数据库迁移文件。
  • models.py: 定义模型。
  • tests.py: 编写测试代码。
  • views.py: 定义视图函数或类。

1.3 初探魔法地图:第一个Django项目“Hello Universe”

  • 路由配置:给URL贴上门牌号

  • 视图函数:从“Hello World”到“Hello Universe”

  • 调试模式:当代码炸了,如何优雅甩锅给IDE

现在,让我们开始创建我们的第一个Django项目,并构建一个简单的“Hello Universe”应用。

1.3.1 路由配置:给URL贴上门牌号

URL配置是Django应用的重要组成部分,它将URL路径映射到相应的视图函数或类。

1.3.1.1 配置项目URL

在 myproject/urls.py 中,添加以下代码:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('myapp.urls')),
]

这里,我们配置了Django admin后台的URL,并包含了 myapp 应用的URL配置。

1.3.1.2 配置应用URL

在 myapp 应用中,创建 urls.py 文件,并添加以下代码:

from django.urls import path
from . import views

urlpatterns = [
    path('', views.hello_universe, name='hello_universe'),
]

这将根路径(/)映射到 hello_universe 视图函数。

1.3.2 视图函数:从“Hello World”到“Hello Universe”

视图函数负责处理请求、执行业务逻辑、调用模型获取数据,并将数据传递给模板进行渲染。

在 myapp/views.py 中,添加以下代码:

from django.shortcuts import render

def hello_universe(request):
    return render(request, 'hello_universe.html', {})

这里,我们定义了一个名为 hello_universe 的视图函数,它使用 render 函数将请求和模板 hello_universe.html 进行渲染。

1.3.3 模板:HTML的“乐高积木”哲学

模板是Django应用的前端部分,负责将数据渲染成HTML页面。

在 myapp/templates/ 目录下,创建 hello_universe.html 文件,并添加以下代码:

<!DOCTYPE html>
<html>
<head>
    <title>Hello Universe</title>
</head>
<body>
    <h1>Hello, Universe!</h1>
    <p>Welcome to the world of Django!</p>
</body>
</html>

1.3.4 运行项目

1.3.4.1 迁移数据库

在项目根目录下,运行以下命令:

python manage.py migrate

这将应用默认的数据库迁移,创建必要的数据库表。

1.3.4.2 运行开发服务器
python manage.py runserver

这将启动Django的开发服务器,默认情况下,服务器会在 http://127.0.0.1:8000/ 上运行。

打开浏览器,访问 http://127.0.0.1:8000/,你应该会看到“Hello, Universe!”的页面。

1.3.5 调试模式:当代码炸了,如何优雅甩锅给IDE

在开发过程中,代码难免会出现错误。Django提供了强大的调试工具,可以帮助你快速定位和解决问题。

1.3.5.1 错误处理

当代码出现错误时,Django会显示一个详细的错误页面,包含错误信息、堆栈跟踪、局部变量等信息。

1.3.5.2 调试工具栏

Django Debug Toolbar 是一个非常有用的工具,可以提供关于请求、SQL查询、缓存、模板渲染等详细信息。

1.3.5.2.1 安装 Django Debug Toolbar

pip install django-debug-toolbar

1.3.5.2.2 配置 Django Debug Toolbar

在 settings.py 中,添加以下配置:

INSTALLED_APPS = [
    ...
    'debug_toolbar',
    ...
]

MIDDLEWARE = [
    ...
    'debug_toolbar.middleware.DebugToolbarMiddleware',
    ...
]

INTERNAL_IPS = [
    '127.0.0.1',
]

在 myproject/urls.py 中,添加以下配置:

from django.urls import path, include

urlpatterns = [
    ...
    path('__debug__/', include('debug_toolbar.urls')),
]

1.3.5.2.3 使用 Django Debug Toolbar

启动开发服务器后,页面右侧会出现一个调试工具栏,你可以点击不同的按钮来查看各种调试信息。

本章小结

恭喜你完成了第一章的学习!在这一章中,我们了解了Python和Django的基础知识,搭建了开发环境,并创建了我们的第一个Django项目“Hello Universe”。你已经迈出了成为Django魔法师的重要一步。

在接下来的章节中,我们将深入探讨Django的各个组成部分,包括模型、模板、视图、URL配置、数据库迁移、admin后台等。你将学习如何构建更复杂的应用,并掌握Django的高级功能,例如RESTful API、异步任务、性能优化等。

记住,魔法并非一蹴而就,需要不断地学习和实践。相信自己,你一定能够掌握Django的魔法,成为一名优秀的Django开发者!

希望你喜欢本章的内容,并从中受益。下一章,我们将深入探讨数据模型和数据库交互,敬请期待!

第二章:数据炼金术士必修课

欢迎来到第二章——数据炼金术士必修课。在这一章中,你将深入学习Django的数据处理核心,掌握如何将数据模型转化为强大的“魔法”,并学会如何与各种数据库进行交互,最终为你的应用提供坚实的数据基础。

2.1 ORM炼金术:将数据模型点石成金

  • Model设计:用类定义你的“数据DNA”

  • 字段类型:字符串、时间、外键的魔法属性

  • 数据库迁移:让模型变身SQL的变形咒

Django的ORM(对象关系映射)是其最强大的特性之一。它让你能够使用Python类来定义数据库表和关系,而无需编写繁琐的SQL语句。这就像是将数据模型点石成金,将枯燥的数据库操作转化为优雅的Python代码。

2.1.1 Model设计:用类定义你的“数据DNA”

模型(Model)是Django应用的核心,它定义了应用的数据结构和数据之间的关系。就像DNA决定了生物体的结构和功能一样,模型决定了应用的数据组织和行为。

2.1.1.1 创建一个简单的模型

让我们以一个博客应用为例,创建一个简单的 Post 模型。

# myapp/models.py

from django.db import models
from django.contrib.auth.models import User

class Post(models.Model):
    STATUS_CHOICES = [
        ('draft', 'Draft'),
        ('published', 'Published'),
    ]

    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200, unique=True)
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts')
    content = models.TextField()
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')

    class Meta:
        ordering = ['-created']
        indexes = [
            models.Index(fields=['-created']),
        ]

    def __str__(self):
        return self.title

2.1.1.1.1 字段类型

  • CharField: 用于存储字符串。max_length 参数是必需的,用于定义字段的最大长度。
  • SlugField: 用于存储slug,通常用于URL中。unique=True 参数确保每个slug都是唯一的。
  • ForeignKey: 用于创建外键关系。on_delete=models.CASCADE 参数指定当关联的对象被删除时,相关的对象也会被删除。related_name 参数允许你从关联的对象反向访问该对象。
  • TextField: 用于存储大量文本。
  • DateTimeField: 用于存储日期和时间。auto_now_add=True 参数在对象创建时自动设置字段为当前时间。auto_now=True 参数在对象每次保存时自动更新字段为当前时间。
  • CharField (choices): 用于存储具有预定义选项的字符串。choices 参数是一个包含元组的列表,每个元组包含一个值和一个可读的名称。

2.1.1.1.2 元类选项

  • ordering: 指定查询集返回对象的默认排序方式。-created 表示按 created 字段降序排序。
  • indexes: 定义数据库索引,可以提高查询性能。

2.1.1.1.3 __str__ 方法

__str__ 方法定义了对象的可读字符串表示形式,在Django admin后台和Django shell中非常有用。

2.1.1.2 关系字段

Django提供了三种主要的关系字段:

  • ForeignKey: 一对多关系。
  • ManyToManyField: 多对多关系。
  • OneToOneField: 一对一关系。

2.1.1.2.1 一对多关系

在我们的博客应用中,Post 模型通过 author 字段与 User 模型建立了一对多关系。一个用户可以拥有多篇文章,而一篇文章只能有一个作者。

2.1.1.2.2 多对多关系

假设我们有一个 Category 模型,我们可以使用 ManyToManyField 来为 Post 模型添加多对多关系。

# myapp/models.py

class Category(models.Model):
    name = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(max_length=100, unique=True)

    class Meta:
        ordering = ['name']

    def __str__(self):
        return self.name

class Post(models.Model):
    ...
    categories = models.ManyToManyField(Category, related_name='posts', blank=True)
    ...

2.1.1.2.3 一对一关系

假设我们有一个 Profile 模型,用于存储用户的额外信息,我们可以使用 OneToOneField 来建立一对一关系。

# myapp/models.py

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    bio = models.TextField(blank=True)
    website = models.URLField(blank=True)

    def __str__(self):
        return f"{self.user.username}'s profile"
2.1.2 字段类型:字符串、时间、外键的魔法属性

Django提供了丰富的字段类型,每种类型都有其特定的用途和属性。以下是一些常用的字段类型及其属性:

2.1.2.1 字符串字段
  • CharField: 用于存储字符串。max_length 参数是必需的。
  • TextField: 用于存储大量文本。
  • SlugField: 用于存储slug,通常用于URL中。max_length 默认值为 50,unique=True 经常使用。
  • EmailField: 用于存储电子邮件地址。max_length 默认值为 254。
2.1.2.2 数字字段
  • IntegerField: 用于存储整数。
  • BigIntegerField: 用于存储大整数。
  • DecimalField: 用于存储固定精度的十进制数。max_digits 和 decimal_places 参数是必需的。
  • FloatField: 用于存储浮点数。
2.1.2.3 日期和时间字段
  • DateField: 用于存储日期。auto_now 和 auto_now_add 参数经常使用。
  • DateTimeField: 用于存储日期和时间。
  • TimeField: 用于存储时间。
2.1.2.4 布尔字段
  • BooleanField: 用于存储布尔值。
  • NullBooleanField: 允许存储 NULL 值。
2.1.2.5 文件和图像字段
  • FileField: 用于存储文件。upload_to 参数是必需的,用于指定文件上传的路径。
  • ImageField: 用于存储图像。upload_to 参数是必需的。
2.1.2.6 其他字段
  • UUIDField: 用于存储UUID。
  • URLField: 用于存储URL。
  • JSONField: 用于存储JSON数据。
2.1.3 数据库迁移:让模型变身SQL的变形咒

数据库迁移是将模型的变化应用到数据库的过程。Django的迁移系统非常强大,可以自动生成迁移文件,并将其应用到数据库中。

2.1.3.1 生成迁移文件

当你对模型进行更改后,需要生成迁移文件。

python manage.py makemigrations

这将扫描你的模型,并生成相应的迁移文件。

2.1.3.2 应用迁移
python manage.py migrate

这将应用所有未应用的迁移,将模型的变化应用到数据库中。

2.1.3.3 回滚迁移

如果你需要回滚到之前的迁移状态,可以使用以下命令:

python manage.py migrate myapp 0001_initial

这将回滚 myapp 应用到 0001_initial 迁移。

2.1.3.4 伪迁移

有时候,你可能需要创建一个空的迁移文件,用于执行自定义的数据库操作。

python manage.py makemigrations --empty myapp

这将在 myapp 应用中创建一个空的迁移文件。

2.2 三神器降临:MySQL × MongoDB × Redis

  • MySQL驯龙记:Django连接、事务与复杂查询优化

  • MongoDB野性召唤:用Djongo征服NoSQL大陆

  • Redis闪电术:缓存、会话与实时排行榜实战

Django对多种数据库提供了良好的支持,包括关系型数据库(如MySQL、PostgreSQL)和NoSQL数据库(如MongoDB)。此外,Redis作为一种内存数据库和缓存解决方案,在Django应用中也有着广泛的应用。

2.2.1 MySQL驯龙记:Django连接、事务与复杂查询优化
2.2.1.1 配置 MySQL

在 settings.py 中,配置数据库连接:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mydatabase',
        'USER': 'mydatabaseuser',
        'PASSWORD': 'mypassword',
        'HOST': 'localhost',
        'PORT': '3306',
        'OPTIONS': {
            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
        },
    }
}
2.2.1.2 迁移数据库
python manage.py migrate
2.2.1.3 事务管理

Django 提供了强大的事务管理机制,可以确保数据库操作的原子性。

from django.db import transaction

def my_view(request):
    with transaction.atomic():
        # 执行数据库操作
        ...
2.2.1.4 复杂查询优化

2.2.1.4.1 避免 N+1 查询问题

N+1 查询问题是ORM常见的问题,会导致大量的数据库查询,影响性能。

问题示例:

# 不良实践
posts = Post.objects.all()
for post in posts:
    print(post.author.username)

这将导致1个查询获取所有文章,然后对每篇文章执行1个查询获取作者信息,总共执行N+1个查询。

解决方案:

使用 select_related 或 prefetch_related 来减少查询次数。

# 优化后的查询
posts = Post.objects.select_related('author').all()
for post in posts:
    print(post.author.username)

这将只执行2个查询:1个获取所有文章,1个获取所有相关的作者信息。

2.2.1.4.2 使用 Q 对象进行复杂查询

Q 对象允许你构建复杂的查询条件。

from django.db.models import Q

posts = Post.objects.filter(
    Q(title__icontains='django') | Q(content__icontains='django')
)

这将查询标题或内容中包含 “django” 的所有文章。

2.2.2 MongoDB野性召唤:用Djongo征服NoSQL大陆

Django对NoSQL数据库的支持不如关系型数据库完善,但通过使用第三方库,如 Djongo,可以让你在Django中使用MongoDB。

2.2.2.1 安装 Djongo
pip install djongo
2.2.2.2 配置 MongoDB

在 settings.py 中,配置数据库连接:

DATABASES = {
    'default': {
        'ENGINE': 'djongo',
        'NAME': 'mydatabase',
        'HOST': 'localhost',
        'PORT': 27017,
        'USER': 'mydatabaseuser',
        'PASSWORD': 'mypassword',
    }
}
2.2.2.3 使用 MongoDB 模型

Django的ORM与MongoDB的文档模型并不完全兼容,但 Djongo 提供了对大多数ORM功能的支持。

from djongo import models

class Post(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200, unique=True)
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts')
    content = models.TextField()
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
    categories = models.ArrayReferenceField(
        to='Category',
        on_delete=models.CASCADE,
        related_name='posts',
    )

    class Meta:
        ordering = ['-created']
        indexes = [
            models.Index(fields=['-created']),
        ]

    def __str__(self):
        return self.title

注意: 使用 Djongo 时,需要注意一些限制,例如不支持某些复杂的查询和关系。

2.2.3 Redis闪电术:缓存、会话与实时排行榜实战

Redis是一种内存数据库,常用于缓存、会话存储和实时数据处理。

2.2.3.1 安装 Redis

在大多数Linux发行版上,可以使用以下命令安装 Redis:

sudo apt-get install redis-server
2.2.3.2 配置 Redis

在 settings.py 中,配置 Redis 作为缓存后端:

CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379/1',
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
        }
    }
}
2.2.3.3 使用 Redis 作为缓存
from django.core.cache import cache

# 设置缓存
cache.set('my_key', 'my_value', timeout=300)

# 获取缓存
value = cache.get('my_key')
2.2.3.4 使用 Redis 作为会话存储
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'
2.2.3.5 使用 Redis 实现实时排行榜

假设我们有一个游戏应用,需要实现一个实时排行榜。

import redis
from django.conf import settings

# 连接 Redis
r = redis.StrictRedis(host='localhost', port=6379, db=0)

def update_leaderboard(user_id, score):
    r.zadd('leaderboard', {user_id: score})

def get_leaderboard():
    return r.zrevrange('leaderboard', 0, 9, withscores=True)

2.3 Admin后台:给非程序员老板的“上帝视角”

  • 定制Admin界面:从毛坯房到精装修

  • 数据批量操作:一键拯救996的摸鱼时刻

  • 权限控制:别让实习生删了生产库!

Django admin后台是一个强大的工具,可以让你无需编写任何代码,就能为你的应用创建一个功能齐全的管理界面。

2.3.1 定制 Admin界面:从毛坯房到精装修
2.3.1.1 注册模型到 Admin

在 myapp/admin.py 中,注册模型:

from django.contrib import admin
from .models import Post, Category, Profile

admin.site.register(Post)
admin.site.register(Category)
admin.site.register(Profile)
2.3.1.2 自定义 Admin 界面
2.3.1.2.1 列表显示
class PostAdmin(admin.ModelAdmin):
    list_display = ('title', 'author', 'status', 'created', 'updated')
    list_filter = ('status', 'author', 'created', 'updated')
    search_fields = ('title', 'content')
    prepopulated_fields = {'slug': ('title',)}
    raw_id_fields = ('author',)
    date_hierarchy = 'created'
    ordering = ('status', 'created')

admin.site.register(Post, PostAdmin)
2.3.1.2.2 内联编辑
class ProfileInline(admin.StackedInline):
    model = Profile
    can_delete = False
    verbose_name_plural = 'profile'

class UserAdmin(admin.ModelAdmin):
    inlines = (ProfileInline,)

admin.site.unregister(User)
admin.site.register(User, UserAdmin)
2.3.1.2.3 自定义表单
from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = '__all__'
        widgets = {
            'content': forms.Textarea(attrs={'cols': 40, 'rows': 20}),
        }

class PostAdmin(admin.ModelAdmin):
    form = PostForm

admin.site.register(Post, PostAdmin)
2.3.2 数据批量操作:一键拯救996的摸鱼时刻

Django admin后台支持批量操作,可以让你对多个对象执行相同的操作。

2.3.2.1 自定义批量操作
class PostAdmin(admin.ModelAdmin):
    ...

    actions = ['make_published']

    def make_published(self, request, queryset):
        queryset.update(status='published')
    make_published.short_description = "Mark selected posts as published"

admin.site.register(Post, PostAdmin)
2.3.3 权限控制:别让实习生删了生产库!

Django admin后台提供了强大的权限控制机制,可以根据用户角色分配不同的权限。

2.3.3.1 配置权限

在 settings.py 中,配置用户权限:

AUTHENTICATION_BACKENDS = [
    'django.contrib.auth.backends.ModelBackend',
]

# 权限组
from django.contrib.auth.models import Group, Permission

def create_groups():
    group, created = Group.objects.get_or_create(name='editor')
    permission = Permission.objects.get(codename='add_post')
    group.permissions.add(permission)
    ...
2.3.3.2 自定义权限
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from .models import Post

content_type = ContentType.objects.get_for_model(Post)
permission = Permission.objects.create(
    codename='can_publish',
    name='Can Publish Posts',
    content_type=content_type,
)
2.3.3.3 分配权限
from django.contrib.auth.models import User, Group

user = User.objects.get(username='alice')
group = Group.objects.get(name='editor')
user.groups.add(group)

小结

在这一章中,我们深入探讨了Django的ORM系统,了解了如何设计模型、配置数据库、迁移模型以及使用不同的数据库后端。我们还学习了如何使用Django admin后台来管理数据,并通过权限控制来保护你的应用。

掌握这些知识,你将能够构建更加复杂和强大的Django应用。在下一章中,我们将学习如何让网页跳起踢踏舞,奏响前后端交响曲,敬请期待!

第三章:让网页跳起踢踏舞,奏响前后端交响曲

欢迎来到第三章——让网页跳起踢踏舞,奏响前后端交响曲。在这一章中,我们将深入探讨Django的MTV架构中的“T”和“V”,即模板(Template)和视图(View)。你将学习如何利用模板引擎构建动态网页,如何设计视图来处理各种请求,以及如何通过表单和验证来确保用户输入的安全性和有效性。

3.1 模板引擎:HTML的“乐高积木”哲学

  • 变量与标签:动态内容的灵魂注入

  • 继承与组件:拒绝复制粘贴的重复人生

  • 过滤器:给数据化妆的100种姿势

Django的模板引擎就像是一个HTML的“乐高积木”套装,它允许你将静态的HTML页面转化为动态的、可复用的组件。通过使用变量、标签、继承和过滤器,你可以轻松地创建复杂且功能强大的网页。

3.1.1 变量与标签:动态内容的灵魂注入

变量和标签是Django模板中注入动态内容的主要方式。

3.1.1.1 变量

变量用于在模板中显示从视图传递过来的数据。变量被双大括号包围,例如 {{ variable }}

示例:

假设我们有一个视图,它传递了一个名为 post 的上下文变量:

# myapp/views.py

from django.shortcuts import render, get_object_or_404
from .models import Post

def post_detail(request, post_id):
    post = get_object_or_404(Post, id=post_id)
    return render(request, 'post_detail.html', {'post': post})

在模板 post_detail.html 中,我们可以这样使用变量:

<!DOCTYPE html>
<html>
<head>
    <title>{{ post.title }}</title>
</head>
<body>
    <h1>{{ post.title }}</h1>
    <p>By {{ post.author.username }} on {{ post.created|date:"F j, Y" }}</p>
    <div>
        {{ post.content|linebreaks }}
    </div>
</body>
</html>

解释:

  • {{ post.title }}: 显示文章的标题。
  • {{ post.author.username }}: 显示作者的用户名。
  • {{ post.created|date:"F j, Y" }}: 使用日期过滤器格式化发布日期。
  • {{ post.content|linebreaks }}: 使用 linebreaks 过滤器将换行符转换为 HTML 换行标签。
3.1.1.2 标签

标签用于在模板中执行更复杂的逻辑,例如循环、条件判断、包含其他模板等。

常用标签:

  • for: 用于循环遍历列表或查询集。

    <ul>
        {% for comment in post.comments.all %}
            <li>{{ comment.author }}: {{ comment.content }}</li>
        {% empty %}
            <li>No comments yet.</li>
        {% endfor %}
    </ul>
    
  • if: 用于条件判断。

    {% if post.status == 'published' %}
        <p>This post is published.</p>
    {% else %}
        <p>This post is a draft.</p>
    {% endif %}
    
  • include: 用于包含其他模板。

    {% include "sidebar.html" %}
    
  • block 和 extends: 用于模板继承(将在下一节中详细讨论)。

3.1.2 继承与组件:拒绝复制粘贴的重复人生

Django模板引擎支持模板继承和组件化开发,这使得你可以创建可复用的模板片段,避免重复代码。

3.1.2.1 模板继承

模板继承允许你定义一个“基础模板”,其中包含网站通用的元素,例如头部、导航栏、底部等。然后,其他模板可以继承这个基础模板,并覆盖或添加特定的内容。

示例:

基础模板 (base.html):

<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}My Site{% endblock %}</title>
    <link rel="stylesheet" href="{% static 'css/styles.css' %}">
</head>
<body>
    <header>
        <h1>My Site</h1>
        <nav>
            <ul>
                <li><a href="{% url 'home' %}">Home</a></li>
                <li><a href="{% url 'blog' %}">Blog</a></li>
                <!-- 其他导航链接 -->
            </ul>
        </nav>
    </header>
    <main>
        {% block content %}
        <!-- 子模板内容将在这里显示 -->
        {% endblock %}
    </main>
    <footer>
        <p>&copy; 2023 My Site</p>
    </footer>
</body>
</html>

子模板 (post_detail.html):

{% extends "base.html" %}

{% block title %}{{ post.title }} - My Site{% endblock %}

{% block content %}
    <article>
        <h2>{{ post.title }}</h2>
        <p>By {{ post.author.username }} on {{ post.created|date:"F j, Y" }}</p>
        <div>
            {{ post.content|linebreaks }}
        </div>
    </article>
{% endblock %}

解释:

  • {% extends "base.html" %}: 指定该模板继承自 base.html
  • {% block title %}...{% endblock %}: 重写基础模板中的 title 块。
  • {% block content %}...{% endblock %}: 重写基础模板中的 content 块。
3.1.2.2 组件化

组件化开发是将网页分解成可复用的组件,例如导航栏、侧边栏、页脚等。

示例:

导航栏组件 (navbar.html):

<nav>
    <ul>
        <li><a href="{% url 'home' %}">Home</a></li>
        <li><a href="{% url 'blog' %}">Blog</a></li>
        <!-- 其他导航链接 -->
    </ul>
</nav>

使用组件:

{% include "navbar.html" %}

3.1.3 过滤器:给数据化妆的100种姿势

过滤器用于在模板中修改变量的输出。Django提供了许多内置过滤器,你也可以创建自定义过滤器。

3.1.3.1 内置过滤器

以下是一些常用的内置过滤器:

  • date: 格式化日期。

    {{ post.created|date:"F j, Y" }}
    
  • linebreaks: 将换行符转换为 HTML 换行标签。

    {{ post.content|linebreaks }}
    
  • safe: 标记字符串为安全的 HTML。

    {{ post.content|safe }}
    
  • length: 返回变量的长度。

    {{ post.comments.all|length }}
    
  • default: 如果变量未定义或为空,则使用默认值。

    {{ post.summary|default:"No summary available." }}
    
  • pluralize: 根据变量的值返回复数形式。

    {{ post.comments.all|length }} comment{{ post.comments.all|length|pluralize }}
    
3.1.3.2 自定义过滤器

你可以创建自定义过滤器来扩展模板功能。

步骤:

1. 在应用目录下创建一个 templatetags 目录,并添加一个 __init__.py 文件。

myapp/
    templatetags/
        __init__.py
        my_filters.py

2. 在 my_filters.py 中定义过滤器。

from django import template

register = template.Library()

@register.filter(name='capitalize')
def capitalize(value):
    return value.capitalize()

3. 在模板中加载并使用自定义过滤器。

{% load my_filters %}

<p>{{ "hello world"|capitalize }}</p>

输出:

<p>Hello world</p>

    3.2 视图的千层套路

    • 函数视图 vs 类视图:牛仔与贵族的对决

    • 请求响应循环:Django的“心脏跳动”原理

    • 文件上传与下载:小心内存爆炸的隐藏陷阱

    视图是Django应用的核心组件,负责处理HTTP请求、执行业务逻辑、调用模型获取数据,并将数据传递给模板进行渲染。Django支持两种主要的视图类型:函数视图和类视图。

    3.2.1 函数视图 vs 类视图:牛仔与贵族的对决
    3.2.1.1 函数视图

    函数视图是Django中最基本的视图类型,它以函数的形式存在,接受一个 request 对象作为参数,并返回一个 HttpResponse 对象。

    示例:

    from django.shortcuts import render, get_object_or_404
    from .models import Post
    
    def post_detail(request, post_id):
        post = get_object_or_404(Post, id=post_id)
        return render(request, 'post_detail.html', {'post': post})
    

    优点:

    • 简单直观: 易于理解和编写。
    • 灵活性: 可以处理各种复杂的逻辑。

    缺点:

    • 代码重复: 对于相似的请求处理逻辑,可能会导致代码重复。
    • 可扩展性差: 难以复用和扩展。
    3.2.1.2 类视图

    类视图是Django提供的一种更高级的视图类型,它基于面向对象编程的思想,将视图组织成类和方法。

    示例:

    from django.views.generic import DetailView
    from .models import Post
    
    class PostDetailView(DetailView):
        model = Post
        template_name = 'post_detail.html'
        context_object_name = 'post'
    

    优点:

    • 代码复用: 可以通过继承和组合来复用代码。
    • 可扩展性: 更容易扩展和修改。
    • 内置功能: 提供了许多内置的功能,例如表单处理、权限控制等。

    缺点:

    • 复杂性: 对于简单任务,可能显得过于复杂。
    • 学习曲线: 需要理解面向对象编程和Django的类视图体系。
    3.2.1.3 选择建议
    • 对于简单的任务,函数视图可能更合适。
    • 对于复杂的任务,类视图可以提供更好的组织和可维护性。
    3.2.2 请求响应循环:Django的“心脏跳动”原理

    Django的请求响应循环是Django应用的核心机制,它描述了Django如何处理HTTP请求并返回HTTP响应。

    步骤:

    1.URL 解析: Django接收HTTP请求后,会根据URL配置找到对应的视图函数或类。

    2.视图处理: 视图函数或类处理请求,执行业务逻辑,调用模型获取数据。

    3.模板渲染: 视图将数据传递给模板进行渲染,生成HTML内容。

    4.响应返回: Django将生成的HTML内容封装成HTTP响应,返回给客户端。

      图示:

      HTTP Request
            |
            V
         URL Dispatcher
            |
            V
         View Function/Class
            |
            V
         Model (Database)
            |
            V
         Template Rendering
            |
            V
      HTTP Response
      
      3.2.3 文件上传与下载:小心内存爆炸的隐藏陷阱

      处理文件上传和下载是Web应用常见的需求,但需要谨慎处理,以避免内存泄漏和安全问题。

      3.2.3.1 文件上传

      步骤:

      1. 配置表单:

      <form enctype="multipart/form-data" method="post" action="{% url 'upload' %}">
          {% csrf_token %}
          <input type="file" name="myfile">
          <button type="submit">Upload</button>
      </form>
      

      2. 视图处理:

      from django.shortcuts import render
      from django.core.files.storage import FileSystemStorage
      
      def upload_file(request):
          if request.method == 'POST' and request.FILES['myfile']:
              myfile = request.FILES['myfile']
              fs = FileSystemStorage()
              filename = fs.save(myfile.name, myfile)
              return render(request, 'upload_success.html', {'filename': filename})
          return render(request, 'upload.html')
      

        注意事项:

        • 安全性: 验证上传的文件类型和大小,防止恶意文件上传。
        • 存储: 使用专用的存储服务,例如 Amazon S3,来存储上传的文件,避免占用服务器存储空间。
        • 性能: 对于大文件上传,考虑使用异步处理或分片上传。
        3.2.3.2 文件下载

        步骤:

        1. 视图处理:

        from django.http import FileResponse, Http404
        
        def download_file(request, filename):
            filepath = os.path.join(settings.MEDIA_ROOT, filename)
            if os.path.exists(filepath):
                return FileResponse(open(filepath, 'rb'), as_attachment=True)
            raise Http404
        

          注意事项:

          • 安全性: 验证请求的合法性,防止未授权的下载。
          • 性能: 对于大文件下载,考虑使用流式响应,避免占用过多的内存。
          • 权限控制: 确保只有有权限的用户才能下载特定的文件。

          3.3 表单与验证:用户输入的安全结界

          • 表单设计:从零到防机器人攻击

          • CSRF防御:别让黑客偷走你的曲奇(Cookie)

          • 自定义验证器:比如“密码不许用123456”

          表单是Web应用与用户交互的重要方式。Django提供了强大的表单处理机制,包括表单设计、验证、CSRF保护等。

          3.3.1 表单设计:从零到防机器人攻击
          3.3.1.1 使用 Django 表单

          Django提供了两种主要的表单类型: forms.Form 和 forms.ModelForm

          示例:

          from django import forms
          from .models import Post
          
          class PostForm(forms.ModelForm):
              class Meta:
                  model = Post
                  fields = ['title', 'content', 'status']
          
          3.3.1.2 使用表单视图
          from django.shortcuts import render, redirect
          from .forms import PostForm
          
          def create_post(request):
              if request.method == 'POST':
                  form = PostForm(request.POST)
                  if form.is_valid():
                      form.save()
                      return redirect('post_list')
              else:
                  form = PostForm()
              return render(request, 'post_form.html', {'form': form})
          
          3.3.1.3 渲染表单
          <form method="post">
              {% csrf_token %}
              {{ form.as_p }}
              <button type="submit">Submit</button>
          </form>
          
          3.3.2 CSRF防御:别让黑客偷走你的曲奇(Cookie)

          CSRF(跨站请求伪造)是一种常见的网络攻击,Django内置了CSRF保护机制。

          3.3.2.1 使用 {% csrf_token %}

          在表单中添加 {% csrf_token %},Django会自动生成一个 CSRF token,并将其包含在表单中。

          <form method="post">
              {% csrf_token %}
              ...
          </form>
          
          3.3.2.2 配置 CSRF 中间件

          Django默认启用了 CSRF 中间件。如果没有启用,可以在 settings.py 中添加:

          MIDDLEWARE = [
              ...
              'django.middleware.csrf.CsrfViewMiddleware',
              ...
          ]
          
          3.3.3 自定义验证器:比如“密码不许用123456”

          Django允许你创建自定义验证器来验证表单字段。

          示例:

          from django.core.exceptions import ValidationError
          import re
          
          def validate_password(value):
              if re.match(r'123456', value):
                  raise ValidationError('Password cannot be 123456')
          

          使用自定义验证器:

          from django import forms
          from .validators import validate_password
          
          class UserForm(forms.ModelForm):
              password = forms.CharField(widget=forms.PasswordInput, validators=[validate_password])
              ...
          

          小结

          在这一章中,我们深入探讨了Django的模板引擎和视图系统,学习了如何使用变量、标签、继承和过滤器来构建动态网页,以及如何处理文件上传和下载。我们还学习了如何使用表单和验证来确保用户输入的安全性和有效性。

          掌握这些知识,你将能够构建更加复杂和用户友好的Django应用。在下一章中,我们将学习如何构建RESTful API,敬请期待!

          第四章:进阶!黑魔法防御术

          欢迎来到第四章——进阶!黑魔法防御术。在这一章中,我们将深入探索Django的高级功能,学习如何将Django应用转化为一个强大的“数据快递员”,如何处理异步任务以提升用户体验,以及如何优化应用性能,使其从“蜗牛”进化为“猎豹”。同时,我们还将探讨一些“黑魔法防御术”,例如RESTful API、异步任务和性能优化,以确保你的应用能够应对各种挑战。

          4.1 RESTful API:让Django化身数据快递员

          • DRF(Django REST Framework)入门

          • 序列化器:把模型变成JSON的翻译官

          • JWT认证:没有Session的世界怎么活?

          在当今的Web开发中,构建RESTful API已经成为一项必备技能。RESTful API允许你将应用的数据和功能暴露给其他应用或前端框架,例如React、Vue.js等。Django本身并不直接支持RESTful API,但借助强大的第三方库——Django REST framework(DRF),我们可以轻松地实现这一目标。

          4.1.1 DRF(Django REST Framework)入门

          4.1.1.1 安装 DRF

          首先,我们需要安装DRF:

          pip install djangorestframework
          
          4.1.1.2 配置 DRF

          在 settings.py 中,添加 'rest_framework' 到 INSTALLED_APPS

          INSTALLED_APPS = [
              ...
              'rest_framework',
              ...
          ]
          
          4.1.1.3 创建第一个 API

          假设我们有一个 Post 模型,我们希望为其创建一个API端点。

          步骤:

          1. 创建序列化器(Serializer):

          序列化器负责将模型实例转换为JSON格式,并验证和反序列化来自客户端的数据。

          # myapp/serializers.py
          
          from rest_framework import serializers
          from .models import Post
          
          class PostSerializer(serializers.ModelSerializer):
              class Meta:
                  model = Post
                  fields = ['id', 'title', 'slug', 'author', 'content', 'created', 'updated', 'status']
          

          解释:

          • serializers.ModelSerializer: DRF提供的基于模型的序列化器类。
          • fields: 指定要序列化的字段。

          2. 创建视图(View):

          DRF提供了多种视图集(ViewSet)和通用视图(Generic Views)来简化API视图的编写。

          # myapp/views.py
          
          from rest_framework import viewsets
          from .models import Post
          from .serializers import PostSerializer
          
          class PostViewSet(viewsets.ModelViewSet):
              queryset = Post.objects.all()
              serializer_class = PostSerializer
          

          解释:

          • viewsets.ModelViewSet: 提供对模型的所有CRUD(创建、读取、更新、删除)操作的默认实现。
          • queryset: 指定视图集操作的查询集。
          • serializer_class: 指定用于序列化和反序列化的序列化器类。

          3. 配置 URL 路由:

          使用 DRF 提供的 DefaultRouter 来自动生成URL路由。

          # myapp/urls.py
          
          from django.urls import path, include
          from rest_framework import routers
          from .views import PostViewSet
          
          router = routers.DefaultRouter()
          router.register(r'posts', PostViewSet)
          
          urlpatterns = [
              path('api/', include(router.urls)),
          ]
          

          解释:

          • routers.DefaultRouter(): 自动生成一组URL路由,包括列表视图和详细视图。
          • router.register(r'posts', PostViewSet): 注册 PostViewSet 到 posts 路由。

          4. 访问 API:

          启动开发服务器后,访问 http://127.0.0.1:8000/api/posts/,你将看到API的JSON响应。

          示例:

          [
              {
                  "id": 1,
                  "title": "Hello, World!",
                  "slug": "hello-world",
                  "author": 1,
                  "content": "This is my first post.",
                  "created": "2023-10-01T12:00:00Z",
                  "updated": "2023-10-01T12:00:00Z",
                  "status": "published"
              },
              ...
          ]
          

            4.1.2 序列化器:把模型变成JSON的翻译官

            序列化器是DRF的核心组件,负责将复杂的数据类型,例如模型实例,转换为JSON格式,并处理输入数据的验证和反序列化。

            4.1.2.1 序列化器的类型
            • ModelSerializer: 基于模型的序列化器,自动生成字段和验证器。
            • Serializer: 手动定义字段和验证器的序列化器。
            • HyperlinkedModelSerializer: 基于超链接的序列化器,适用于RESTful API。
            4.1.2.2 序列化器字段

            DRF提供了丰富的字段类型,例如:

            • BooleanField: 布尔字段。
            • CharField: 字符串字段。
            • IntegerField: 整数字段。
            • DateTimeField: 日期时间字段。
            • ForeignKey: 外键字段。
            • ManyToManyField: 多对多字段。

            示例:

            class CommentSerializer(serializers.ModelSerializer):
                class Meta:
                    model = Comment
                    fields = ['id', 'post', 'author', 'content', 'created']
            
            4.1.2.3 嵌套序列化器

            你可以嵌套序列化器,以表示模型之间的关系。

            示例:

            class PostSerializer(serializers.ModelSerializer):
                comments = CommentSerializer(many=True, read_only=True)
            
                class Meta:
                    model = Post
                    fields = ['id', 'title', 'slug', 'author', 'content', 'comments', 'created', 'updated', 'status']
            
            4.1.2.4 验证器

            序列化器支持自定义验证器,用于验证输入数据。

            示例:

            from rest_framework import serializers
            from django.core.exceptions import ValidationError
            
            def validate_title(value):
                if "badword" in value:
                    raise serializers.ValidationError("Title contains a bad word.")
                return value
            
            class PostSerializer(serializers.ModelSerializer):
                title = serializers.CharField(validators=[validate_title])
            
                class Meta:
                    model = Post
                    fields = ['id', 'title', 'slug', 'author', 'content', 'created', 'updated', 'status']
            

            4.1.3 JWT认证:没有Session的世界怎么活?

            传统的Django应用依赖于Session和Cookie来进行用户认证和授权。然而,在构建API时,使用JWT(JSON Web Token)进行认证更加合适,因为它是无状态的,并且可以跨域使用。

            4.1.3.1 什么是 JWT?

            JWT是一种紧凑的、自包含的方式,用于在各方之间安全地传输信息。它由三部分组成:

            1.Header: 包含令牌的类型和签名算法。

            2.Payload: 包含声明(claims),例如用户ID、过期时间等。

            3.Signature: 用于验证令牌的安全性。

              4.1.3.2 安装 JWT 库

              我们将使用 djangorestframework-simplejwt 库来实现JWT认证。

              pip install djangorestframework-simplejwt
              
              4.1.3.3 配置 JWT

              在 settings.py 中,配置DRF的认证类:

              REST_FRAMEWORK = {
                  'DEFAULT_AUTHENTICATION_CLASSES': (
                      'rest_framework_simplejwt.authentication.JWTAuthentication',
                  ),
              }
              
              4.1.3.4 创建 JWT 端点

              在 myapp/urls.py 中,添加以下内容:

              from rest_framework_simplejwt.views import (
                  TokenObtainPairView,
                  TokenRefreshView,
              )
              
              urlpatterns = [
                  ...
                  path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
                  path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
              ]
              
              4.1.3.5 使用 JWT 认证

              客户端可以通过以下步骤获取和使用JWT令牌:

              1. 获取 Token:

              发送 POST 请求到 /api/token/,包含 username 和 password

              示例:

              {
                  "username": "alice",
                  "password": "password123"
              }
              

              响应:

              {
                  "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
                  "refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
              }
              

              2. 使用 Token:

              在后续的 API 请求中,在 HTTP 头部添加 Authorization: Bearer <access_token>

              示例:

              Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
              

              3. 刷新 Token:

              当访问令牌过期后,可以使用刷新令牌获取新的访问令牌。

              发送 POST 请求到 /api/token/refresh/,包含 refresh 令牌。

              示例:

              {
                  "refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
              }
              

              响应:

              {
                  "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
              }
              
                4.1.3.6 保护 API 视图

                DRF 提供了 @permission_classes 装饰器来保护API视图。

                示例:

                from rest_framework.permissions import IsAuthenticated
                from rest_framework.views import APIView
                from rest_framework.response import Response
                
                class ProtectedView(APIView):
                    permission_classes = [IsAuthenticated]
                
                    def get(self, request):
                        return Response({"message": "Hello, authenticated user!"})
                

                4.2 异步任务:别让用户等到花儿都谢了

                • Celery异步队列:后台任务的隐形斗篷

                • WebSocket实战:用Django Channels实现聊天室

                在Web应用中,有些任务可能需要较长时间才能完成,例如发送电子邮件、处理文件上传、生成报告等。如果在请求-响应周期中处理这些任务,会导致用户等待时间过长,影响用户体验。

                4.2.1 Celery异步队列:后台任务的隐形斗篷

                Celery 是一个强大的异步任务队列,可以与Django无缝集成,用于处理后台任务。

                4.2.1.1 安装 Celery
                pip install celery
                
                4.2.1.2 配置 Celery

                在项目根目录下创建 celery.py 文件:

                # myproject/celery.py
                
                from __future__ import absolute_import, unicode_literals
                import os
                from celery import Celery
                
                os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
                
                app = Celery('myproject')
                
                app.config_from_object('django.conf:settings', namespace='CELERY')
                app.autodiscover_tasks()
                

                在 myproject/__init__.py 中,添加以下内容:

                # myproject/__init__.py
                
                from .celery import app as celery_app
                
                __all__ = ('celery_app',)
                

                在 settings.py 中,添加以下配置:

                CELERY_BROKER_URL = 'redis://localhost:6379/0'
                CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
                
                4.2.1.3 创建异步任务
                # myapp/tasks.py
                
                from celery import shared_task
                
                @shared_task
                def send_email(subject, message, to):
                    # 发送电子邮件的逻辑
                    pass
                
                4.2.1.4 调用异步任务
                from .tasks import send_email
                
                def my_view(request):
                    ...
                    send_email.delay(subject, message, to)
                    ...
                
                4.2.1.5 运行 Celery
                celery -A myproject worker --loglevel=info
                
                4.2.2 WebSocket实战:用Django Channels实现聊天室

                WebSocket 是一种在单个TCP连接上进行全双工通信的协议,适用于实时应用,例如聊天室、实时通知等。Django Channels 提供了对WebSocket的支持。

                4.2.2.1 安装 Django Channels
                pip install channels
                
                4.2.2.2 配置 Channels

                在 settings.py 中,添加 'channels' 到 INSTALLED_APPS

                INSTALLED_APPS = [
                    ...
                    'channels',
                    ...
                ]
                

                设置 ASGI 应用:

                ASGI_APPLICATION = 'myproject.asgi.application'
                
                4.2.2.3 创建 ASGI 应用

                在项目根目录下创建 asgi.py 文件:

                # myproject/asgi.py
                
                import os
                from channels.auth import AuthMiddlewareStack
                from channels.routing import ProtocolTypeRouter, URLRouter
                from django.core.asgi import get_asgi_application
                import myapp.routing
                
                os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
                
                application = ProtocolTypeRouter({
                    "http": get_asgi_application(),
                    "websocket": AuthMiddlewareStack(
                        URLRouter(
                            myapp.routing.websocket_urlpatterns
                        )
                    ),
                })
                
                4.2.2.4 配置路由

                在 myapp/routing.py 中,添加以下内容:

                # myapp/routing.py
                
                from django.urls import re_path
                from . import consumers
                
                websocket_urlpatterns = [
                    re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_view()),
                ]
                
                4.2.2.5 创建消费者
                # myapp/consumers.py
                
                import json
                from channels.generic.websocket import AsyncWebsocketConsumer
                
                class ChatConsumer(AsyncWebsocketConsumer):
                    async def connect(self):
                        self.room_name = self.scope['url_route']['kwargs']['room_name']
                        self.room_group_name = f'chat_{self.room_name}'
                
                        # 加入房间
                        await self.channel_layer.group_add(
                            self.room_group_name,
                            self.channel
                        )
                
                        await self.accept()
                
                    async def disconnect(self, close_code):
                        # 离开房间
                        await self.channel_layer.group_discard(
                            self.room_group_name,
                            self.channel
                        )
                
                    # 接收消息
                    async def receive(self, text_data):
                        data = json.loads(text_data)
                        message = data['message']
                
                        # 发送消息到房间
                        await self.channel_layer.group_send(
                            self.room_group_name,
                            {
                                'type': 'chat_message',
                                'message': message
                            }
                        )
                
                    # 处理消息
                    async def chat_message(self, event):
                        message = event['message']
                
                        # 发送消息给客户端
                        await self.send(text_data=json.dumps({
                            'message': message
                        }))
                
                4.2.2.6 前端代码
                <!DOCTYPE html>
                <html>
                <head>
                    <title>Chat Room</title>
                </head>
                <body>
                    <h1>Chat Room</h1>
                    <textarea id="chat-log" cols="100" rows="20"></textarea><br>
                    <input id="chat-message-input" type="text" size="100">
                    <input id="chat-message-submit" type="button" value="Send">
                    <script>
                        var chatSocket = new WebSocket(
                            'ws://' + window.location.host + '/ws/chat/lobby/');
                
                        chatSocket.onmessage = function(e) {
                            var data = JSON.parse(e.data);
                            var message = data['message'];
                            document.getElementById('chat-log').value += (message + '\n');
                        };
                
                        chatSocket.onclose = function(e) {
                            console.log('Chat socket closed');
                        };
                
                        document.getElementById('chat-message-submit').onclick = function(e) {
                            var messageInputDom = document.getElementById('chat-message-input');
                            var message = messageInputDom.value;
                            chatSocket.send(JSON.stringify({
                                'message': message
                            }));
                            messageInputDom.value = '';
                        };
                    </script>
                </body>
                </html>
                

                4.3 性能优化:从蜗牛到猎豹的进化论

                • 查询优化:N+1问题的末日审判

                • 缓存策略:Redis、Memcached与CDN的三角恋

                • Gunicorn × Nginx:高并发下的生存法则

                性能优化是Web应用开发中至关重要的一环,它直接影响着用户体验和服务器成本。

                4.3.1 查询优化:N+1问题的末日审判

                N+1查询问题是ORM常见的问题,会导致大量的数据库查询,影响性能。

                4.3.1.1 什么是 N+1 查询问题?

                假设我们有一个 Author 和 Book 模型,每个作者有多本书。

                # models.py
                
                class Author(models.Model):
                    name = models.CharField(max_length=100)
                
                class Book(models.Model):
                    title = models.CharField(max max_length=100)
                    author = models.ForeignKey(Author, on_delete=models.CASCADE)
                

                问题示例:

                # 不良实践
                authors = Author.objects.all()
                for author in authors:
                    books = author.book_set.all()
                    print(author.name, [book.title for book in books])
                

                这将导致1个查询获取所有作者,然后对每个作者执行1个查询获取其书籍,总共执行N+1个查询。

                4.3.1.2 解决方案

                4.3.1.2.1 使用 select_related

                select_related 用于执行 SQL JOIN 操作,以减少查询次数。

                authors = Author.objects.select_related('book_set').all()
                for author in authors:
                    books = author.book_set.all()
                    print(author.name, [book.title for book in books])
                

                这将只执行2个查询:1个获取所有作者,1个获取所有相关的书籍。

                4.3.1.2.2 使用 prefetch_related

                prefetch_related 用于执行子查询或缓存查询,以减少查询次数。

                authors = Author.objects.prefetch_related('book_set').all()
                for author in authors:
                    books = author.book_set.all()
                    print(author.name, [book.title for book in books])
                

                这将执行2个查询:1个获取所有作者,1个获取所有相关的书籍。

                4.3.1.2.3 使用 iterator 迭代器

                对于大型查询集,可以使用 iterator 来减少内存消耗。

                authors = Author.objects.iterator()
                for author in authors:
                    books = author.book_set.all()
                    print(author.name, [book.title for book in books])
                

                注意: 使用 iterator 时,无法进行反向查询。

                4.3.2 缓存策略:Redis、Memcached与CDN的三角恋

                缓存是提升性能的重要手段。Django支持多种缓存后端,例如 Redis、Memcached 和 CDN。

                4.3.2.1 Redis

                Redis 是一个内存数据库,常用于缓存、会话存储和实时数据处理。

                4.3.2.1.1 配置 Redis

                在 settings.py 中,配置 Redis 作为缓存后端:

                CACHES = {
                    'default': {
                        'BACKEND': 'django_redis.cache.RedisCache',
                        'LOCATION': 'redis://127.0.0.1:6379/1',
                        'OPTIONS': {
                            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
                        }
                    }
                }
                

                4.3.2.1.2 使用 Redis 缓存

                from django.core.cache import cache
                
                # 设置缓存
                cache.set('my_key', 'my_value', timeout=300)
                
                # 获取缓存
                value = cache.get('my_key')
                
                4.3.2.2 Memcached

                Memcached 是一个高性能的分布式内存对象缓存系统。

                4.3.2.2.1 配置 Memcached

                CACHES = {
                    'default': {
                        'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
                        'LOCATION': '127.0.0.1:11211',
                    }
                }
                

                4.3.2.2.2 使用 Memcached 缓存

                from django.core.cache import cache
                
                # 设置缓存
                cache.set('my_key', 'my_value', timeout=300)
                
                # 获取缓存
                value = cache.get('my_key')
                
                4.3.2.3 CDN

                CDN(内容分发网络)用于将静态资源分发到全球各地的服务器,以减少延迟,提高加载速度。

                4.3.2.3.1 配置 CDN

                在 settings.py 中,配置静态文件 URL:

                STATIC_URL = 'https://cdn.example.com/myapp/'
                
                STATICFILES_STORAGE = 'myapp.storage.CDNStorage'
                

                4.3.2.3.2 使用 CDN 存储

                # myapp/storage.py
                
                from  django  import  forms
                from  django  import  storage
                from  django.conf import  settings
                from  storages.backends  import  s3boto3
                
                class  CDNStorage(s3boto3  .S3Boto3Storage):
                    location  =  'static'
                    default_acl  =  'public-read'
                    bucket_name  =  'my-cdn-bucket'
                
                4.3.3 Gunicorn × Nginx:高并发下的生存法则

                为了应对高并发请求,我们需要使用更强大的应用服务器和反向代理服务器,例如 Gunicorn 和 Nginx。

                4.3.3.1 安装 Gunicorn
                pip install gunicorn
                
                4.3.3.2 运行 Gunicorn
                gunicorn myproject.wsgi:application --bind 0.0.0.0:8000 --workers 4
                

                解释:

                • myproject.wsgi:application: 指定 WSGI 应用入口。
                • --bind 0.0.0.0:8000: 绑定到指定端口。
                • --workers 4: 设置工作进程数。
                4.3.3.3 配置 Nginx

                在 Nginx 配置文件中,添加以下内容:

                server {
                    listen 80;
                    server_name example.com;
                
                    location / {
                        proxy_pass http://127.0.0.1:8000;
                        proxy_set_header Host $host;
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                    }
                
                    location /static/ {
                        alias /path/to/static/;
                    }
                
                    location /media/ {
                        alias /path/to/media/;
                    }
                }
                

                解释:

                • server: 定义一个服务器块。
                • listen 80: 监听端口 80。
                • server_name: 指定服务器名称。
                • location /: 处理所有请求,转发到 Gunicorn。
                • location /static/ 和 location /media/: 处理静态文件和媒体文件。
                4.3.3.4 使用 HTTPS

                为了确保安全,建议使用 HTTPS。可以使用 Let's Encrypt 提供的免费 SSL 证书。

                4.3.3.4.1 安装 Certbot

                sudo apt-get install certbot python3-certbot-nginx
                

                4.3.3.4.2 获取 SSL 证书

                sudo certbot --nginx -d example.com
                

                4.3.3.4.3 自动续订

                Certbot 会自动设置续订任务。

                小结

                在这一章中,我们深入探讨了Django的高级功能,包括构建RESTful API、处理异步任务以及优化性能。通过学习DRF、Celery、Django Channels、Redis、Memcached、CDN、Gunicorn 和 Nginx,你将能够构建更加复杂和高效的Django应用。

                掌握这些知识,你将能够应对各种复杂的开发场景,并构建出高性能、可扩展的Web应用。

                第五章:出征!代码的星辰大海

                欢迎来到第五章——出征!代码的星辰大海。在这一章中,我们将踏上一段激动人心的旅程,从将Django应用部署到云端,到学习如何保护应用免受攻击,再到探索微服务架构和未来趋势。你将学习如何将你的应用从本地小白变成服务器大神,如何构建坚固的“防弹衣”来保护你的代码,以及如何让你的应用在未来保持竞争力。

                5.1 部署到云端:从本地小白到服务器大神

                • Docker化:把你的应用塞进集装箱

                • AWS/GCP/Aliyun选型指南:哪朵云最懂你的钱包

                • GitHub Actions:自动化部署的机械仆从

                将应用部署到云端是将你的“魔法”展现给全世界的重要一步。在这一节中,我们将探讨如何使用Docker容器化应用,如何选择合适的云服务提供商,以及如何利用GitHub Actions实现自动化部署。

                5.1.1 Docker化:把你的应用塞进集装箱

                Docker是一种流行的容器化技术,它可以将你的应用及其所有依赖项打包到一个轻量级、可移植的容器中。

                5.1.1.1 为什么要使用 Docker?
                • 一致性: 确保开发、测试和生产环境的一致性。
                • 可移植性: 容器可以在任何支持Docker的环境中运行。
                • 隔离性: 容器之间相互隔离,避免依赖冲突。
                • 可扩展性: 轻松地扩展和缩减容器数量。
                5.1.1.2 安装 Docker

                在大多数Linux发行版上,可以使用以下命令安装Docker:

                sudo apt-get update
                sudo apt-get install docker-ce docker-ce-cli containerd.io
                
                5.1.1.3 编写 Dockerfile

                在项目根目录下创建一个名为 Dockerfile 的文件,并添加以下内容:

                # 使用官方 Python 镜像作为基础镜像
                FROM python:3.11-slim
                
                # 设置环境变量
                ENV PYTHONDONTWRITEBYTECODE 1
                ENV PYTHONUNBUFFERED 1
                
                # 设置工作目录
                WORKDIR /app
                
                # 复制依赖文件并安装依赖
                COPY requirements.txt .
                RUN pip install --upgrade pip
                RUN pip install -r requirements.txt
                
                # 复制项目代码
                COPY . .
                
                # 收集静态文件
                RUN python manage.py collectstatic --noinput
                
                # 运行 Gunicorn
                CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000"]
                

                解释:

                • FROM python:3.11-slim: 使用官方Python 3.11 精简版镜像作为基础镜像。
                • ENV: 设置环境变量,禁止写入字节码,并启用无缓冲模式。
                • WORKDIR: 设置工作目录为 /app
                • COPY requirements.txt .: 复制 requirements.txt 文件到工作目录。
                • RUN pip install ...: 安装依赖包。
                • COPY . .: 复制项目代码到工作目录。
                • RUN python manage.py collectstatic --noinput: 收集静态文件。
                • CMD: 启动 Gunicorn 服务器。
                5.1.1.4 构建 Docker 镜像
                docker build -t my-django-app .
                
                5.1.1.5 运行 Docker 容器
                docker run -d -p 8000:8000 --name my-django-container my-django-app
                

                解释:

                • -d: 后台运行容器。
                • -p 8000:8000: 将主机的 8000 端口映射到容器的 8000 端口。
                • --name: 指定容器名称。
                • my-django-app: 使用之前构建的镜像。
                5.1.1.6 使用 Docker Compose

                对于更复杂的应用,可以使用 Docker Compose 来管理多个容器。

                示例 docker-compose.yml 文件:

                version: '3'
                
                services:
                  web:
                    build: .
                    command: gunicorn myproject.wsgi:application --bind 0.0.0.0:8000
                    volumes:
                      - .:/app
                    ports:
                      - "8000:8000"
                    depends_on:
                      - db
                    environment:
                      - DJANGO_SETTINGS_MODULE=myproject.settings
                      - DEBUG=1
                
                  db:
                    image: postgres:14
                    volumes:
                      - postgres_data:/var/lib/postgresql/data/
                    environment:
                      - POSTGRES_DB=mydatabase
                      - POSTGRES_USER=mydatabaseuser
                      - POSTGRES_PASSWORD=mypassword
                
                volumes:
                  postgres_data:
                

                运行 Docker Compose:

                docker-compose up -d
                

                5.1.2 AWS/GCP/Aliyun选型指南:哪朵云最懂你的钱包

                选择合适的云服务提供商是部署应用的关键决策。以下是三大主流云服务提供商的比较:

                5.1.2.1 Amazon Web Services (AWS)

                优点:

                • 服务丰富: 提供超过200种服务,涵盖计算、存储、数据库、网络、人工智能等。
                • 市场占有率: 是目前市场份额最大的云服务提供商。
                • 生态系统: 拥有庞大的生态系统和支持社区。

                缺点:

                • 复杂性: 对于新手来说,学习曲线较陡。
                • 成本: 成本较高,尤其是对于小型项目。
                5.1.2.2 Google Cloud Platform (GCP)

                优点:

                • 数据分析和机器学习: 在数据分析和机器学习方面具有强大的优势。
                • 性价比: 对于某些工作负载,GCP 可能更具性价比。
                • 创新性: 经常推出新的服务和功能。

                缺点:

                • 市场份额: 市场占有率低于 AWS 和 Aliyun。
                • 生态系统: 生态系统不如 AWS 成熟。
                5.1.2.3 Aliyun (阿里云)

                优点:

                • 本地化服务: 针对中国市场提供本地化的服务和解决方案。
                • 性价比: 对于中国用户来说,Aliyun 通常更具性价比。
                • 支持中文: 提供中文支持和服务。

                缺点:

                • 全球覆盖: 全球覆盖范围不如 AWS 和 GCP。
                • 生态系统: 生态系统不如 AWS 和 GCP 成熟。
                5.1.2.4 选择建议
                • 如果你需要丰富的服务和支持,并且不介意支付更高的费用,AWS 是一个不错的选择。
                • 如果你对数据分析和机器学习有需求,GCP 可能更适合你。
                • 如果你主要面向中国市场,并且希望获得本地化的服务和支持,Aliyun 是理想的选择。

                5.1.3 GitHub Actions:自动化部署的机械仆从

                GitHub Actions 是一个强大的自动化工具,可以让你在代码库中定义工作流,实现自动化部署、测试、构建等任务。

                5.1.3.1 什么是 GitHub Actions?

                GitHub Actions 允许你在代码库中创建工作流(workflow),每个工作流由一个或多个作业(job)组成,每个作业包含多个步骤(step)。你可以使用预定义的动作(action)来简化工作流的编写。

                5.1.3.2 编写 GitHub Actions 工作流

                在项目根目录下创建 .github/workflows/deploy.yml 文件,并添加以下内容:

                name: Deploy to AWS
                
                on:
                  push:
                    branches:
                      - main
                
                jobs:
                  build:
                    runs-on: ubuntu-latest
                
                    steps:
                      - name: Checkout code
                        uses: actions/checkout@v3
                
                      - name: Set up Docker Buildx
                        uses: docker/setup-buildx-action@v2
                
                      - name: Login to Docker Hub
                        uses: docker/login-action@v2
                        with:
                          username: ${{ secrets.DOCKER_USERNAME }}
                          password: ${{ secrets.DOCKER_PASSWORD }}
                
                      - name: Build and push Docker image
                        uses: docker/build-push-action@v4
                        with:
                          push: true
                          tags: my-dockerhub-username/my-django-app:latest
                
                  deploy:
                    runs-on: ubuntu-latest
                    needs: build
                    steps:
                      - name: Checkout code
                        uses: actions/checkout@v3
                
                      - name: Configure AWS credentials
                        uses: aws-actions/configure-aws-credentials@v1
                        with:
                          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
                          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
                          aws-region: us-east-1
                
                      - name: Deploy to Elastic Beanstalk
                        uses: actions/setup-python@v4
                        with:
                          python-version: '3.11'
                
                      - name: Install awsebcli
                        run: |
                          pip install awsebcli --upgrade --user
                
                      - name: Initialize Elastic Beanstalk
                        run: |
                          eb init -p docker my-django-app --region us-east-1
                
                      - name: Create or update environment
                        run: |
                          eb create my-django-app-env --single --instance_type t2.micro --region us-east-1
                          eb use my-django-app-env
                          eb deploy
                

                解释:

                • on: 定义触发工作流的事件,这里是 push 到 main 分支。
                • jobs: 定义工作流中的作业。
                  • build: 构建和推送 Docker 镜像。
                    • Checkout code: 检出代码。
                    • Set up Docker Buildx: 设置 Docker Buildx。
                    • Login to Docker Hub: 登录 Docker Hub。
                    • Build and push Docker image: 构建并推送 Docker 镜像。
                  • deploy: 部署到 AWS Elastic Beanstalk。
                    • Checkout code: 检出代码。
                    • Configure AWS credentials: 配置 AWS 凭证。
                    • Deploy to Elastic Beanstalk: 使用 Elastic Beanstalk CLI 进行部署。
                5.1.3.3 配置 GitHub Secrets

                在 GitHub 仓库中,导航到 Settings > Secrets and variables > Actions,添加以下密钥:

                • DOCKER_USERNAME: Docker Hub 用户名
                • DOCKER_PASSWORD: Docker Hub 密码
                • AWS_ACCESS_KEY_ID: AWS 访问密钥 ID
                • AWS_SECRET_ACCESS_KEY: AWS 秘密访问密钥

                5.2 测试与安全:代码的疫苗与防弹衣

                • 单元测试:别等上线才当Debug侠

                • XSS、SQL注入防御:黑客的套路拆解

                • 日志监控:当系统崩了,如何优雅甩锅(第二季)

                测试和安全是软件开发生命周期的关键组成部分,它们就像代码的“疫苗”和“防弹衣”,可以保护你的应用免受各种攻击和错误。

                5.2.1 单元测试:别等上线才当Debug侠

                单元测试是测试代码的基本单元,例如函数、方法或类,以确保其按预期工作。

                5.2.1.1 编写单元测试

                Django 提供了强大的测试框架, 基于 Python 的 unittest 模块。

                示例:

                from django.test import TestCase
                from .models import Post
                
                class PostModelTest(TestCase):
                    def test_create_post(self):
                        post = Post.objects.create(
                            title="Test Post",
                            slug="test-post",
                            author_id=1,
                            content="This is a test post.",
                            status='published'
                        )
                        self.assertEqual(post.title, "Test Post")
                        self.assertEqual(post.slug, "test-post")
                        self.assertEqual(post.author_id, 1)
                        self.assertEqual(post.content, "This is a test post.")
                        self.assertEqual(post.status, 'published')
                
                5.2.1.2 运行单元测试
                python manage.py test
                
                5.2.1.3 使用 Django 测试客户端

                Django 提供了测试客户端,可以模拟浏览器行为,测试视图和URL配置。

                示例:

                from django.test import TestCase, Client
                from django.urls import reverse
                
                class PostViewTest(TestCase):
                    def setUp(self):
                        self.client = Client()
                        self.post = Post.objects.create(
                            title="Test Post",
                            slug="test-post",
                            author_id=1,
                            content="This is a test post.",
                            status='published'
                        )
                
                    def test_post_detail(self):
                        response = self.client.get(reverse('post_detail', args=[self.post.id]))
                        self.assertEqual(response.status_code, 200)
                        self.assertContains(response, "Test Post")
                        self.assertContains(response, "This is a test post.")
                

                5.2.2 XSS、SQL注入防御:黑客的套路拆解

                Web应用面临着各种安全威胁,其中XSS(跨站脚本)和SQL注入是最常见的攻击方式。

                5.2.2.1 XSS 攻击

                XSS 攻击是指攻击者将恶意脚本注入到网页中,窃取用户信息或执行其他恶意操作。

                防御措施:

                • 使用 Django 模板自动转义: Django 模板默认会对变量进行转义,防止XSS攻击。
                • 避免使用 safe 过滤器: 除非你确定内容是安全的,否则不要使用 safe 过滤器。
                • 使用内容安全策略 (CSP): 配置CSP头,限制脚本执行。

                示例:

                # 设置 CSP 头
                from django.middleware import security
                
                def my_view(request):
                    response = render(request, 'my_template.html')
                    response['Content-Security-Policy'] = "default-src 'self'"
                    return response
                
                5.2.2.2 SQL 注入

                SQL 注入是指攻击者通过操纵输入数据,向数据库中注入恶意SQL代码。

                防御措施:

                • 使用 Django ORM: Django ORM 会自动处理参数化查询,防止SQL注入。
                • 避免使用 extra() 方法: 除非必要,否则不要使用 extra() 方法,因为它可能导致SQL注入。
                • 使用 Q 对象: 使用 Q 对象构建查询,可以避免SQL注入。

                示例:

                from django.db.models import Q
                
                def my_view(request):
                    query = request.GET.get('q')
                    posts = Post.objects.filter(Q(title__icontains=query) | Q(content__icontains=query))
                    return render(request, 'post_list.html', {'posts': posts})
                

                5.2.3 日志监控:当系统崩了,如何优雅甩锅(第二季)

                日志监控是监控应用运行状态、排查错误和性能瓶颈的重要手段。

                5.2.3.1 配置日志

                在 settings.py 中,配置日志:

                import logging
                import logging.config
                
                LOGGING = {
                    'version': 1,
                    'disable_existing_loggers': False,
                    'formatters': {
                        'verbose': {
                            'format': '{levelname} {asctime} {module} {message}',
                            'style': '{',
                        },
                    },
                    'handlers': {
                        'file': {
                            'level': 'INFO',
                            'class': 'logging.FileHandler',
                            'filename': 'django.log',
                            'formatter': 'verbose',
                        },
                    },
                    'loggers': {
                        'django': {
                            'handlers': ['file'],
                            'level': 'INFO',
                            'propagate': True,
                        },
                    },
                }
                
                5.2.3.2 使用日志
                import logging
                
                logger = logging.getLogger(__name__)
                
                def my_view(request):
                    logger.info("User accessed my_view")
                    ...
                
                5.2.3.3 使用 Sentry 进行错误监控

                Sentry 是一个流行的错误监控工具,可以帮助你实时跟踪和修复错误。

                安装 Sentry:

                pip install sentry-sdk
                

                配置 Sentry:

                import sentry_sdk
                from sentry_sdk.integrations.django import DjangoIntegration
                
                sentry_sdk.init(
                    dsn="https://examplePublicKey@o0.ingest.sentry.io/0",
                    integrations=[DjangoIntegration()],
                    traces_sample_rate=1.0,
                )
                
                5.2.3.4 配置监控和报警

                使用云服务提供商提供的监控工具,例如 AWS CloudWatch、GCP Stackdriver、Aliyun CloudMonitor,来设置报警规则,监控关键指标,例如 CPU 使用率、内存使用率、响应时间等。

                5.3 从单体到微服务:Django的二次觉醒

                • 微服务架构:拆!拆!拆!

                • GraphQL进阶:想要啥数据自己点

                • 未来趋势:Serverless与Django的量子纠缠

                随着应用规模的扩大,单体架构可能会面临可扩展性、灵活性和维护性等方面的问题。微服务架构提供了一种更灵活、更可扩展的解决方案。

                5.3.1 微服务架构:拆!拆!拆!

                5.3.1.1 什么是微服务?

                微服务是一种软件架构风格,它将应用拆分为一组松耦合的、小型的、独立的服务,每个服务负责一个特定的业务功能。

                5.3.1.2 微服务的优势
                • 可扩展性: 可以根据每个服务的需求进行独立扩展。
                • 灵活性: 不同服务可以使用不同的技术栈和技术。
                • 维护性: 更易于维护和部署。
                • 团队协作: 不同的团队可以独立开发、测试和部署服务。
                5.3.1.3 微服务的挑战
                • 复杂性: 增加了系统架构的复杂性。
                • 数据一致性: 需要处理分布式数据一致性问题。
                • 服务间通信: 需要处理服务间通信的延迟和可靠性问题。

                5.3.2 GraphQL进阶:想要啥数据自己点

                GraphQL 是一种用于API的查询语言,它允许客户端指定所需的数据,而不是由服务器预先定义数据格式。

                5.3.2.1 什么是 GraphQL?

                GraphQL 允许客户端定义查询结构,服务器根据查询结构返回数据。这使得客户端可以更高效地获取所需的数据,减少网络带宽和延迟。

                5.3.2.2 使用 GraphQL 与 Django

                安装 Graphene-Django:

                pip install graphene-django
                

                配置 Graphene-Django:

                在 settings.py 中,添加 'graphene_django' 到 INSTALLED_APPS

                INSTALLED_APPS = [
                    ...
                    'graphene_django',
                    ...
                ]
                

                在 myproject/urls.py 中,添加以下内容:

                from django.urls import path, include
                from graphene_django.views import GraphQLView
                
                urlpatterns = [
                    ...
                    path('graphql', GraphQLView.as_view(graphiql=True)),
                ]
                

                定义 GraphQL schema:

                # myapp/schema.py
                
                import graphene
                from .models import Post
                from graphene_django import DjangoObjectType
                
                class PostType(DjangoObjectType):
                    class Meta:
                        model = Post
                        fields = ("id", "title", "slug", "author", "content", "created", "updated", "status")
                
                class Query(graphene.ObjectType):
                    posts = graphene.List(PostType)
                
                    def resolve_posts(self, info):
                        return Post.objects.all()
                
                schema = graphene.Schema(query=Query)
                

                5.3.3 未来趋势:Serverless 与 Django 的量子纠缠

                Serverless 是一种云计算模型,它允许你无需管理服务器即可运行代码。

                5.3.3.1 什么是 Serverless?

                Serverless 并不意味着没有服务器,而是指服务器的管理由云服务提供商负责。你只需专注于编写代码,而无需担心服务器配置、扩展和维护。

                5.3.3.2 Serverless 的优势
                • 成本效益: 按需付费,无需为闲置资源付费。
                • 可扩展性: 自动扩展,无需手动干预。
                • 简化运维: 减少运维负担。
                5.3.3.3 Serverless 与 Django

                虽然 Serverless 与 Django 的结合存在一些挑战,例如冷启动时间、长时间运行进程等,但通过使用第三方服务,例如 AWS Lambda、Zappa、Apatche OpenWhisk 等,可以实现 Serverless Django 应用。

                示例:

                使用 Zappa 部署 Django 应用到 AWS Lambda。

                安装 Zappa:

                pip install zappa
                

                配置 Zappa:

                zappa init
                

                部署应用:

                zappa deploy production
                

                小结

                在这一章中,我们探讨了如何将Django应用部署到云端,如何构建坚固的安全防线,以及如何利用微服务架构和Serverless技术来构建现代的、可扩展的应用。

                掌握这些知识,你将能够将你的Django应用提升到一个新的高度,并应对各种复杂的挑战。

                附录:Django魔法师生存工具包

                欢迎来到Django魔法师生存工具包!在这里,我们将为你提供一些在Django开发过程中不可或缺的工具和资源,帮助你更高效地编写代码、调试应用,并从开源项目中汲取灵感。

                F1. 常用命令速查表(migrate? runserver? 拿来吧你!)

                在Django开发过程中,命令行工具是你最亲密的伙伴。以下是一些你绝对不能错过的常用命令:

                F1.1 项目管理
                命令说明
                django-admin startproject myproject创建一个名为 myproject 的新Django项目
                python manage.py startapp myapp在项目中创建一个名为 myapp 的应用
                python manage.py runserver启动开发服务器,默认运行在 http://127.0.0.1:8000/
                python manage.py makemigrations生成数据库迁移文件
                python manage.py migrate应用数据库迁移
                python manage.py createsuperuser创建一个超级用户,用于访问Django admin后台
                python manage.py changepassword username修改指定用户的密码
                python manage.py shell进入Django shell,用于执行Python代码
                python manage.py dbshell进入数据库命令行界面(需要安装相应的数据库客户端)

                示例:

                # 启动开发服务器
                python manage.py runserver
                
                # 创建超级用户
                python manage.py createsuperuser
                
                F1.2 数据库操作
                命令说明
                python manage.py showmigrations显示所有迁移及其状态
                python manage.py sqlmigrate app_label migration_name显示指定迁移的SQL语句
                python manage.py dumpdata导出数据库数据(默认输出到控制台)
                python manage.py loaddata导入数据库数据(从文件或标准输入)
                python manage.py flush清空数据库中的所有数据
                python manage.py dbshell打开数据库命令行界面

                示例:

                # 导出所有数据到 fixtures.json
                python manage.py dumpdata > fixtures.json
                
                # 从 fixtures.json 导入数据
                python manage.py loaddata fixtures.json
                
                F1.3 测试与调试
                命令说明
                python manage.py test运行所有测试
                python manage.py test app_label运行指定应用的测试
                python manage.py test app_label.TestClass.test_method运行指定的测试方法
                python manage.py test --failfast遇到错误时立即停止测试
                python manage.py test --keepdb保留测试数据库,避免重复创建
                python manage.py check检查项目是否存在潜在问题
                python manage.py shell_plus使用 IPython 或其他增强型 shell 进入 Django shell

                示例:

                # 运行所有测试
                python manage.py test
                
                # 运行指定应用的测试
                python manage.py test myapp
                
                # 运行指定的测试方法
                python manage.py test myapp.tests.test_views.MyViewTest.test_get
                
                F1.4 静态文件与媒体文件
                命令说明
                python manage.py collectstatic收集所有静态文件到 STATIC_ROOT 目录
                python manage.py findstatic staticfile查找指定静态文件的位置
                python manage.py compress压缩静态文件(需要使用 django-compressor)
                python manage.py mediagenerator生成媒体文件(需要使用 django-mediaconverter)

                示例:

                # 收集静态文件
                python manage.py collectstatic
                
                F1.5 其他有用命令
                命令说明
                python manage.py check --deploy检查项目是否准备好部署
                python manage.py diffsettings显示当前设置与默认设置的差异
                python manage.py dumpdata --exclude auth.0001_initial导出数据时排除特定迁移
                python manage.py flush --noinput无需确认地清空数据库
                python manage.py sendtestemail发送测试电子邮件
                python manage.py show_urls显示所有URL模式(需要使用 django-extensions)

                示例:

                # 检查项目是否准备好部署
                python manage.py check --deploy
                
                # 显示所有URL模式
                python manage.py show_urls
                

                F2. Django Debug Toolbar:开发者的第三只眼

                Django Debug Toolbar 是一个强大的调试工具,可以帮助你深入了解Django应用的内部运作。它提供了对SQL查询、缓存使用、模板渲染、请求信息等内容的详细分析。

                F2.1 安装 Django Debug Toolbar
                pip install django-debug-toolbar
                
                F2.2 配置 Django Debug Toolbar

                步骤:

                1. 添加到 INSTALLED_APPS:

                INSTALLED_APPS = [
                    ...
                    'debug_toolbar',
                    ...
                ]
                

                2. 添加中间件:

                在 MIDDLEWARE 列表中,添加 DebugToolbarMiddleware,确保它尽可能靠前,但要在 CommonMiddleware 之后。

                MIDDLEWARE = [
                    ...
                    'django.middleware.common.CommonMiddleware',
                    'debug_toolbar.middleware.DebugToolbarMiddleware',
                    ...
                ]
                

                3. 配置内部 IPs:

                INTERNAL_IPS = [
                    '127.0.0.1',
                ]
                

                4. 配置 URL 路由:

                在 myproject/urls.py 中,添加以下内容:

                from django.urls import include, path
                
                urlpatterns = [
                    ...
                    path('__debug__/', include('debug_toolbar.urls')),
                ]
                

                5. 配置静态文件:

                确保 django-debug-toolbar 的静态文件被正确收集和提供。

                STATIC_URL = '/static/'
                
                STATICFILES_DIRS = [
                    ...
                    BASE_DIR / "debug_toolbar",
                ]
                
                  F2.3 使用 Django Debug Toolbar

                  启动开发服务器后,页面右侧会出现一个调试工具栏,包含以下面板:

                  • 版本: 显示Django版本和调试工具栏版本。
                  • 时间: 显示请求处理时间。
                  • SQL: 显示所有SQL查询及其执行时间。
                  • 缓存: 显示缓存命中和未命中情况。
                  • 模板: 显示模板渲染时间和继承关系。
                  • 请求: 显示请求头、请求参数等信息。
                  • 静态文件: 显示静态文件的加载情况。
                  • 设置: 显示Django设置。

                  功能亮点:

                  • SQL 查询分析: 查看每个查询的详细信息,例如执行时间、查询语句、查询结果等。
                  • 缓存使用: 查看缓存命中和未命中情况,以及缓存键和值。
                  • 模板分析: 查看模板渲染的详细过程,包括继承关系、包含的模板、渲染时间等。
                  • 请求信息: 查看请求头、请求参数、cookies、会话信息等。
                  • 静态资源分析: 查看静态资源的加载情况,包括静态文件、媒体文件等。

                  F3. 从开源项目偷师:GitHub宝藏仓库推荐

                  开源项目是学习Django的最佳途径之一。通过研究优秀的开源项目,你可以学习到最佳实践、代码组织方式、架构设计等。以下是一些值得关注的Django开源项目:

                  F3.1 Django REST framework (DRF)

                  链接GitHub - encode/django-rest-framework: Web APIs for Django. 🎸

                  简介:

                  DRF 是一个功能强大的库,用于构建Web API。它提供了序列化器、视图集、路由器、认证、权限、限流等功能,极大地简化了API开发过程。

                  亮点:

                  • 丰富的序列化器: 支持多种序列化器类型,例如 ModelSerializerHyperlinkedModelSerializerSerializer 等。
                  • 强大的视图集: 提供对 CRUD 操作的默认实现,以及自定义视图集的方法。
                  • 灵活的认证和权限: 支持多种认证方式,例如 Token Authentication, Session Authentication, JWT 等,以及细粒度的权限控制。
                  • 内置限流: 支持基于用户、IP 等的限流策略。
                  • 可扩展性: 提供了丰富的扩展点,可以自定义功能。
                  F3.2 Django Channels

                  链接GitHub - django/channels: Developer-friendly asynchrony for Django

                  简介:

                  Channels 为Django引入了对异步和WebSocket的支持,使得构建实时应用,例如聊天应用、实时通知、游戏等成为可能。

                  亮点:

                  • 异步支持: 基于 ASGI 协议,支持异步视图和异步任务。
                  • WebSocket 支持: 提供了对 WebSocket 的原生支持。
                  • 路由系统: 使用与Django URL 相同的路由语法,简化了路由配置。
                  • 消费者模式: 使用消费者(Consumer)来处理 WebSocket 连接和消息。
                  • 集成 Redis: 可以使用 Redis 作为消息层,实现分布式消息传递。
                  F3.3 Django Grappelli

                  链接GitHub - sehmaschine/django-grappelli

                  简介:

                  Grappelli 是一个功能强大的Django admin后台主题,提供了更现代、更美观的界面,以及许多增强功能。

                  亮点:

                  • 现代化界面: 提供了更美观的UI设计。
                  • 可定制的侧边栏: 可以自定义侧边栏内容,例如快捷方式、过滤器等。
                  • 增强的表单控件: 提供了更丰富的表单控件,例如日期选择器、颜色选择器等。
                  • 文件管理: 内置文件管理功能,可以方便地管理上传的文件。
                  • 主题支持: 支持自定义主题,可以根据需要调整界面风格。
                  F3.4 Django Guardian

                  链接GitHub - django-guardian/django-guardian

                  简介:

                  Guardian 为Django提供了对象级权限控制,使得你可以为每个对象分配不同的权限。

                  亮点:

                  • 对象级权限: 支持为每个对象分配不同的权限。
                  • 与 Django 权限系统集成: 可以与Django内置的权限系统无缝集成。
                  • 细粒度的权限控制: 可以控制对单个对象的读取、写入、删除等权限。
                  • 灵活的权限分配: 可以通过代码或Django admin后台分配权限。
                  F3.5 Django Allauth

                  链接https://github.com/pinax/django-allauth

                  简介:

                  Allauth 是一个功能丰富的认证和账户管理库,支持多种认证方式,例如本地账户、社交账户等。

                  亮点:

                  • 多种认证方式: 支持本地账户、社交账户(例如 Facebook, Google, Twitter 等)等多种认证方式。
                  • 账户管理: 提供账户注册、登录、密码重置、邮箱验证等功能。
                  • 社交账户集成: 集成了多种社交账户的认证流程。
                  • 可定制性: 提供了丰富的配置选项,可以根据需要调整认证流程。
                  • 安全性: 实现了多种安全机制,例如防止暴力攻击、CSRF 保护等。
                  F3.6 Django File Uploads

                  链接https://github.com/julen/python-django-file-uploads

                  简介:

                  这是一个用于处理文件上传的Django库,提供了更强大的文件上传功能,例如大文件上传、异步上传、进度跟踪等。

                  亮点:

                  • 大文件上传: 支持分片上传,可以处理大文件。
                  • 异步上传: 使用 AJAX 实现异步文件上传。
                  • 进度跟踪: 提供上传进度跟踪功能。
                  • 安全性: 实现了多种安全机制,例如文件类型验证、文件大小限制等。
                  • 可扩展性: 提供了丰富的扩展点,可以自定义上传行为。

                  在这个附录中,我们为你提供了一些在Django开发过程中非常有用的工具和资源:

                  • 常用命令速查表: 帮助你快速查找常用的Django命令。
                  • Django Debug Toolbar: 为你提供强大的调试功能,助你深入了解Django应用的内部运作。
                  • GitHub宝藏仓库推荐: 让你能够从优秀的开源项目中学习,汲取灵感。

                  希望这些工具和资源能够帮助你更高效地开发Django应用,成为一名真正的Django魔法师!

                    评论 3
                    添加红包

                    请填写红包祝福语或标题

                    红包个数最小为10个

                    红包金额最低5元

                    当前余额3.43前往充值 >
                    需支付:10.00
                    成就一亿技术人!
                    领取后你会自动成为博主和红包主的粉丝 规则
                    hope_wisdom
                    发出的红包
                    实付
                    使用余额支付
                    点击重新获取
                    扫码支付
                    钱包余额 0

                    抵扣说明:

                    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
                    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

                    余额充值