[TOC]


django

个人包装练习的项目

1️⃣ 框架概述

  • Django 是一个用 Python 编写的开源 Web 框架
  • 它遵循 MVC/MVT 架构模式(Model-View-Template)。
  • 目标是快速开发、安全可靠、可扩展的 Web 应用。
  • 官方网站:https://www.djangoproject.com/

2️⃣ 主要特点

  1. 快速开发
    • 内置管理后台(Admin)
    • ORM 映射数据库,免写大量 SQL
    • 内置表单、认证、会话、缓存支持
  2. 安全性高
    • 自动防止 SQL 注入
    • 防止跨站脚本 (XSS)
    • 防止跨站请求伪造 (CSRF)
    • 提供用户认证和权限管理
  3. 可扩展性强
    • 支持插件和第三方库
    • 模块化设计,项目可拆分为多个 App
    • 可与 REST API、GraphQL 等接口集成
  4. 丰富的生态
    • 有 Django REST Framework(DRF)支持 API 开发
    • Django Channels 支持 WebSocket
    • 支持 Celery 异步任务队列

3️⃣ 架构模式

MVT(Model-View-Template)

组件 功能
Model 数据模型,负责数据库操作(ORM 映射表)
View 业务逻辑处理,接收请求,返回响应
Template 前端模板,负责展示 HTML 页面
  • 类似 MVC:
    • Controller 的角色由 View + URL Dispatcher 承担
    • Template 对应 View 层的展示部分

4️⃣ 项目结构

一个典型 Django 项目结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
myproject/
├── manage.py # 命令行工具
├── myproject/ # 项目配置
│ ├── settings.py # 配置文件
│ ├── urls.py # URL 路由
│ ├── wsgi.py # 部署用
│ └── asgi.py # 异步部署用
├── apps/ # 自定义应用
│ └── blog/
│ ├── models.py # 数据模型
│ ├── views.py # 视图/逻辑
│ ├── urls.py # 应用路由
│ └── admin.py # 后台管理
└── templates/ # 模板文件

5️⃣ 核心组件

  1. ORM(Object Relational Mapping)
    • Python 类映射数据库表
    • 支持关系、外键、联合唯一约束、多对多关系
  2. Admin 管理后台
    • 自动生成增删改查后台界面
    • 可通过 admin.py 配置显示字段、搜索、过滤
  3. URL 路由
    • 将请求 URL 映射到 View 函数或类
    • 支持动态参数和命名空间
  4. 中间件(Middleware)
    • 请求和响应处理链
    • 可做认证、日志、压缩、跨域等处理
  5. 模板系统(Template)
    • 渲染 HTML 页面
    • 支持模板继承和自定义标签

6️⃣ 应用场景

  • Web 应用系统(博客、电商、CMS)
  • RESTful API 开发(结合 DRF)
  • 后台管理系统
  • 数据可视化系统
  • 社交平台和微服务

总结:

  • Django 是 Python 最流行的 Web 框架之一
  • 提供全栈开发能力(前端模板 + 后端逻辑 + 数据库)
  • 特点:快速、安全、可扩展、模块化
  • 核心理念:“Don’t repeat yourself(DRY)”

新建项目

库初始化

Django 使用 migrate 命令来初始化数据库。这个命令会应用所有已定义的迁移文件,创建数据库表。

配置数据库

settings.py 文件中,配置数据库连接。默认情况下,Django 使用 SQLite 数据库:

1
2
3
4
5
6
7
8
# settings.py

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}

如果你使用其他数据库(如 PostgreSQL、MySQL 等),需要安装相应的数据库驱动并配置连接参数。例如,使用 PostgreSQL:

1
pip install psycopg2

然后在 settings.py 中配置:

1
2
3
4
5
6
7
8
9
10
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydatabase',
'USER': 'mydatabaseuser',
'PASSWORD': 'mypassword',
'HOST': '127.0.0.1',
'PORT': '5432',
}
}

多数据配置(进阶)

运行迁移

  1. 生成迁移文件(根据你定义的模型生成数据库表结构):
1
python manage.py makemigrations

运行以下命令来初始化数据库:

1
python manage.py migrate

这个命令会创建所有必要的数据库表,包括 Django 自带的用户认证系统表。

清空数据

使用 Django 管理命令

Django 提供了一个 flush 命令,可以清空所有应用的数据。但这个命令会清空整个数据库,而不是特定的应用。如果你只想清空特定应用的数据,可以结合 migrate 命令来实现。

  1. 回滚指定应用的迁移:首先,回滚指定应用的迁移,这将删除该应用的所有表。

    1
    python manage.py migrate myapp zero

    这个命令会将 myapp 的迁移回滚到初始状态,删除该应用的所有表。

  2. 重新应用迁移:然后,重新应用迁移,创建空表。

    1
    python manage.py migrate myapp

    这个命令会重新应用 myapp 的迁移,创建空表

创建管理员用户

使用 createsuperuser 命令创建一个管理员用户:

1
python manage.py createsuperuser

运行这个命令后,系统会提示你输入用户名、邮箱和密码。例如:

1
2
3
4
5
Username (leave blank to use 'yourusername'): admin
Email address: admin@example.com
Password:
Password (again):
Superuser created successfully.

4. 启动开发服务器

启动 Django 开发服务器,访问项目:

1
python manage.py runserver

打开浏览器,访问 http://127.0.0.1:8000/admin,使用刚才创建的管理员用户登录。

模型定义

主键(Primary Key)

每个 Django 模型默认有一个 id 主键字段:

1
id = models.AutoField(primary_key=True)

可以自定义主键:

1
book_id = models.CharField(max_length=20, primary_key=True)

主键保证表中每条记录唯一,用于 ORM 查询、关联外键等。

复合主键(Multi-column PK)

Django 不支持原生多列主键

方法 1(推荐):

  • 给表增加单列主键(id/UUID)。

  • 原来的多列字段加唯一约束:

    1
    2
    3
    4
    5
    6
    7
    8
    class A(models.Model):
    id = models.AutoField(primary_key=True)
    field1 = models.CharField(max_length=20)
    field2 = models.CharField(max_length=20)
    field3 = models.CharField(max_length=20)

    class Meta:
    unique_together = ('field1', 'field2', 'field3')
  • 外键直接关联主键 id

  • 优点:查询、级联、admin 都方便。

方法 2(不推荐):

  • B 表存三列字段对应 A 表的三列。

  • 手动验证组合是否存在:

    1
    2
    if not A.objects.filter(field1=f1, field2=f2, field3=f3).exists():
    raise ValidationError
  • 缺点:无法自动级联,admin 和 ORM 查询不方便。

外键(ForeignKey)

  1. 一对一:表示 一条记录对应另一条记录

    1
    2
    3
    class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    birthday = models.DateField()

    常用于扩展 User

    不想用主键来关联的情况

    在 Django 中,OneToOneField 通常用于建立一对一的关系,它默认关联到目标模型的主键。如果你需要通过非主键字段建立一对一关系,可以使用 ForeignKey 并设置 unique=True这样可以实现类似一对一的关系,但实际上是通过非主键字段进行关联

    1
    2
    3
    4
    5
    6
    7
    8
    9
    from django.db import models
    from django.contrib.auth.models import User

    class UserProfile(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, unique=True)
    birthday = models.DateField()

    def __str__(self):
    return f"{self.user.username}'s profile"

    在这个例子中:

    • user 字段是一个 ForeignKey,关联到 User 模型。
    • unique=True 确保每个 User 只能关联一个 UserProfile,从而实现一对一的关系。

    为什么不能直接使用 OneToOneField 指定非主键字段?

    OneToOneFieldForeignKey 的一个特例,它默认关联到目标模型的主键。Django 的 OneToOneField 不支持直接指定非主键字段作为关联字段。如果你需要通过非主键字段建立一对一关系,必须使用 ForeignKey 并设置 unique=True

    示例:通过非主键字段建立一对一关系

    假设你有一个 User 模型和一个 UserProfile 模型,你希望通过 Useremail 字段建立一对一关系。你可以这样实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    from django.db import models
    from django.contrib.auth.models import User

    class UserProfile(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, unique=True, to_field='email')
    birthday = models.DateField()

    def __str__(self):
    return f"{self.user.username}'s profile"

    在这个例子中:

    • to_field='email' 指定了 User 模型的 email 字段作为关联字段。
    • unique=True 确保每个 Useremail 只能关联一个 UserProfile
  2. 一对多关系:默认关联目标模型的 主键字段id)。

    1
    2
    3
    4
    5
    6
    class Author(models.Model):
    name = models.CharField(max_length=50)

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

    数据库中生成 author_id 列。

    on_delete 常用选项:

    • CASCADE:父表删除,子表删除
    • SET_NULL:父表删除,子表字段置 NULL
    • PROTECT:阻止删除父表
    • SET_DEFAULT:设置默认值
    • DO_NOTHING:不做操作

    • 可用 related_name 设置反向查询:

      1
      author = models.ForeignKey(Author, related_name="books", on_delete=models.CASCADE)
  3. 多对多(ManyToManyField):表示多条记录与多条记录的关系。

    1
    2
    3
    4
    5
    6
    class Book(models.Model):
    title = models.CharField(max_length=100)

    class Reader(models.Model):
    name = models.CharField(max_length=50)
    books = models.ManyToManyField(Book, related_name="readers")

    Django 会自动创建中间关联表,可通过 through 指定自定义关联表。

Django 外键实际关联原理

  • 外键字段对应 目标模型的主键(默认 id)。

  • 数据库中存储 字段名_id

  • 查询时 Django 会自动返回关联对象:

    1
    2
    book = Book.objects.get(id=1)
    print(book.author.name)
  • 反向查询:

    1
    2
    author = Author.objects.get(id=1)
    author.book_set.all() # 默认 related_name=modelname_set

关键点总结表

关系类型 Django 字段类型 对应数据库结构 示例用途
一对多 ForeignKey 表 A.id ↔ 表 B.a_id 订单 → 用户
一对一 OneToOneField 表 A.id ↔ 表 B.a_id 用户 → 用户详情
多对多 ManyToManyField 中间关联表 读者 ↔ 书
主键唯一标识 AutoField/UUIDField 表的唯一列 每条记录唯一
复合主键(推荐方式) AutoField + unique_together 单列主键 + 多列唯一约束 组合字段唯一,方便外键关联

Django Admin

Django 自带的 Admin 管理界面非常强大,可以方便地管理数据库中的数据。以下是一些基本配置:

注册模型

在你的应用的 admin.py 文件中,注册你的模型以便在 Admin 界面中管理。例如:

1
2
3
4
5
# myapp/admin.py
from django.contrib import admin
from .models import MyModel

admin.site.register(MyModel)

自定义 Admin 界面

你可以自定义 Admin 界面的显示方式。例如:

1
2
3
4
5
6
7
8
9
# myapp/admin.py
from django.contrib import admin
from .models import MyModel

@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'created_at')
search_fields = ('name',)
list_filter = ('created_at',)

迁移模型

在 Django 中修改模型后,需要通过迁移(migrations)来更新数据库结构。以下是详细的步骤:

修改模型

首先,修改你的模型。例如,假设你有一个 UserProfile 模型,你想要添加一个新的字段 phone_number

1
2
3
4
5
6
7
8
# models.py
from django.db import models
from django.contrib.auth.models import User

class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
birthday = models.DateField()
phone_number = models.CharField(max_length=15, blank=True, null=True) # 新添加的字段

生成迁移文件

运行以下命令生成迁移文件:

1
python manage.py makemigrations

这个命令会检查你的模型定义,并生成相应的迁移文件。迁移文件会保存在应用的 migrations 文件夹中。例如:

1
2
3
4
myapp/
migrations/
0001_initial.py
0002_add_phone_number.py # 新生成的迁移文件

查看迁移文件

打开生成的迁移文件,确认生成的内容是否符合你的预期。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# myapp/migrations/0002_add_phone_number.py

from django.db import migrations, models

class Migration(migrations.Migration):

dependencies = [
('myapp', '0001_initial'),
]

operations = [
migrations.AddField(
model_name='userprofile',
name='phone_number',
field=models.CharField(max_length=15, blank=True, null=True),
),
]

应用迁移

运行以下命令应用迁移,更新数据库结构:

1
python manage.py migrate

这个命令会应用所有未应用的迁移文件,更新数据库结构。例如:

1
2
3
4
Operations to perform:
Apply all migrations: myapp
Running migrations:
Applying myapp.0002_add_phone_number... OK

验证迁移

访问 Django Admin 或使用 Django Shell 验证迁移是否成功。例如:

1
python manage.py shell

在 Shell 中,你可以检查新字段是否已经添加:

1
2
from myapp.models import UserProfile
UserProfile.objects.create(user=User.objects.get(id=1), birthday='1990-01-01', phone_number='1234567890')

处理数据迁移

如果你需要在迁移过程中处理数据,可以使用 RunPython 操作。例如,假设你需要在添加新字段时初始化一些数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# myapp/migrations/0002_add_phone_number.py

from django.db import migrations, models

def add_default_phone_number(apps, schema_editor):
UserProfile = apps.get_model('myapp', 'UserProfile')
for profile in UserProfile.objects.all():
profile.phone_number = '0000000000'
profile.save()

class Migration(migrations.Migration):

dependencies = [
('myapp', '0001_initial'),
]

operations = [
migrations.AddField(
model_name='userprofile',
name='phone_number',
field=models.CharField(max_length=15, blank=True, null=True),
),
migrations.RunPython(add_default_phone_number),
]

回滚迁移

如果你需要回滚迁移,可以使用以下命令:

1
python manage.py migrate myapp 0001

这个命令会将 myapp 的迁移回滚到 0001_initial 状态。

django test

常见测试类

  1. django.test.TestCase
    • 最常用,继承自 unittest.TestCase,会自动使用测试数据库(运行前建库,结束后清理)。
    • 自带断言方法(如 self.assertContainsself.assertTemplateUsed)。
  2. django.test.SimpleTestCase
    • 不需要数据库时使用(更快)。
    • 适合测试一些工具函数、纯逻辑。
  3. django.test.TransactionTestCase
    • 会在数据库层执行事务测试,适合验证事务回滚逻辑。
    • TestCase 慢。
  4. django.test.LiveServerTestCase
    • 启动一个临时服务,结合 Selenium 等工具做端到端(E2E)测试。

安全相关

美团二面:Cookie、Session、Token、JWT究竟有什么区别?JWT的原理分析及避坑指南

jwt

token