1. ORM介绍
什么是ORM?
ORM,全称 Object-Relational Mapping(对象关系映射),一种通过对象操作数据库的技术。
它的核心思想是:我们不直接写 SQL,而是用 Python 对象(类/实例)来操作数据库表和记录。
ORM 就像一个“翻译官”,帮我们把 Python 代码翻译成数据库能听懂的 SQL 命令。
为什么使用ORM?
Django 中的 ORM 提供了一个高层次、抽象化的接口来操作数据库,它的优势主要包括:
优势 | 说明 |
---|---|
🧠 简单易用 | 用 Python 操作数据库,无需写 SQL |
🛡️ 安全性高 | ORM 自动防止 SQL 注入 |
🛠️ 易于维护 | 数据模型统一管理,字段变化只需改一处 |
🔄 数据库无关 | 可切换 MySQL、PostgreSQL、SQLite 等 |
🔍 强大查询 | 提供丰富的链式查询、聚合、子查询等功能 |
Django中的ORM
在 Django 中,ORM 是与数据库打交道的核心组件,所有数据库操作的起点都是“模型(Model)”
每一个模型(Python类)对应数据库中的一张表
每一个模型的字段(类属性)对应表中的一列
每一个模型的实例对象代表表中的一条记录(row)
ORM 与传统 SQL 的对比:
操作 | SQL 写法 | Django ORM 写法 |
---|---|---|
查询全部 | SELECT * FROM product; |
Product.objects.all() |
插入数据 | INSERT INTO product ... |
Product.objects.create(...) |
更新数据 | UPDATE product SET ... |
product.price = 100; product.save() |
删除数据 | DELETE FROM product ... |
product.delete() |
不用关心连接、游标、关闭这些底层细节,ORM 都帮我们处理好了 。
2. 创建Django项目
使用PyCharm Professional版本能非常便捷的创建django项目:
打开 PyCharm,点击
File > New Project
左侧选择
Django
选择项目路径,例如:
~/Documents/DjangoDemo
创建新虚拟环境,选择Python version
点击Advanced settings > 填写Application name如web
Django项目目录结构
DjangoDemo/
├── DjangoDemo/ ← 项目配置目录(同名目录)
│ ├── __init__.py ← Python 包初始化文件
│ ├── settings.py ← 项目的全局配置文件
│ ├── urls.py ← 全局 URL 路由配置
│ ├── asgi.py ← 异步部署入口(ASGI 服务器用)
│ └── wsgi.py ← 同步部署入口(WSGI 服务器用)
├── web/ ← 你创建的 Django 应用(业务模块)
│ ├── __init__.py ← Python 包初始化文件
│ ├── admin.py ← 注册模型到后台管理站点
│ ├── apps.py ← 应用配置类,系统自动识别用
│ ├── migrations/ ← 数据迁移文件夹(记录模型变更)
│ │ └── __init__.py ← 迁移包初始化
│ ├── models.py ← 数据模型定义(ORM 相关内容)
│ ├── tests.py ← 单元测试写在这里
│ └── views.py ← 视图函数写在这里(处理请求与返回响应)
├── manage.py ← 项目管理脚本,统一入口
└── db.sqlite3 ← 默认数据库文件(开发环境使用)
3. 创建 Django 模型(Model)
什么是模型(Model)?
在 Django 中,模型(Model)就是一个 Python 类,用于定义要存储在数据库中的数据结构。
每一个模型类会被 Django 自动映射为数据库中的一张表,而模型的字段(类属性)就是表中的列(字段)。
写一个模型类 = 定义一张数据库表
添加一个模型字段 = 增加一列字段
项目配置为使用 MySQL 数据库
Django 默认使用 SQLite —— 适合开发,不适合上线。为了保证本地开发环境与线上生产环境一致,避免迁移踩坑,建议学习 ORM 的同时,从一开始就配置 MySQL。
安装 MySQL 驱动
MySql驱动推荐使用 PyMySQL,
纯 Python 写的,跨平台更好安装,适合初学者
打开Pycharm下方的Terminal(会自动进入python虚拟环境),输入以下命令:
pip install pymysql
在项目启动时替代 MySQLdb
在你的项目主配置模块下(例如 DjangoDemo/DjangoDemo/__init__.py
),添加以下代码:
import pymysql
pymysql.install_as_MySQLdb()
修改 settings.py
中数据库配置
在项目的settings文件,如DjangoDemo/DjangoDemo/settings.py中修改DATABASES配置:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 数据库引擎
'NAME': 'django_demo', # 数据库
'USER': 'root', # 用户名
'PASSWORD': 'yourpassword', # 密码
'HOST': '127.0.0.1', # 数据库主机地址
'PORT': '3306', # 数据库端口
'OPTIONS': {
'charset': 'utf8mb4', # 使用支持 Emoji 的字符集
}
}
}
确保数据库已创建
在 MySQL 中创建数据库:
CREATE DATABASE IF NOT EXISTS django_demo CHARSET utf8mb4;
创建一个模型类
假设我们要开发一个电商网站,需要保存商品信息。在web/models.py文件中添加一个Product类:
from django.db import models
# Create your models here.
class Product(models.Model):
name = models.CharField(max_length=100, verbose_name="商品名称") # 字符串字段,限制最大长度为 100
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="价格") # 精确的金额字段,最大 10 位数,小数点后保留 2 位
stock = models.IntegerField(default=0, verbose_name="库存数量") # 库存数量,整型,默认值为 0
is_active = models.BooleanField(default=True, verbose_name="是否上架") # 是否上架,布尔类型
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") # 创建时间,auto_now_add=True 表示创建时自动填充
updated_at = models.DateTimeField(auto_now=True, verbose_name="更新时间") # 更新时间,auto_now=True 表示每次保存都会自动更新
# 在 Django 模型中,如果不写 __str__ 方法,显示出来的是一堆没有意义的“对象地址”
# 加了 __str__ 方法后,增加了信息量,变成更友好的人类可读格式
def __str__(self):
return self.name
class Meta:
db_table = 'product' # 自定义表名,默认表名是 web_product,现在指定为 product
ordering = ['-created_at'] # 默认按创建时间降序排列,['-created_at'] 表示按 created_at 字段降序排列(最新的在前)
verbose_name = "商品" # 后台显示的单数名称,在 Django admin 中显示为“商品”
verbose_name_plural = "商品列表" # 后台显示的复数名称,在 Django admin 列表页显示为“商品列表”
应用模型到数据库
模型写完了,还需要两个步骤:
第一步:创建迁移文件(Django 记录你定义的表结构)
打开Pycharm下方的Terminal,确保当前工作目录在项目根目录,输入以下命令:
python manage.py makemigrations
第二步:执行迁移,真正创建表结构
python manage.py migrate
成功后,Django 会在数据库中创建一张叫 product
的表。如何验证模型是否生效?可以进入 Django shell 测试:
python manage.py shell
from web.models import Product
# 创建一个商品
p = Product(name='苹果手机', price=5999.99, stock=100)
p.save()
# 查询商品
Product.objects.all()
4. 模型(Model)字段解析
字段类型
字符串类型字段(文本类)
字段类型 | SQL 类型 | 说明 |
---|---|---|
CharField |
VARCHAR(n) |
可控长度的短文本,必须指定
|
TextField |
LONGTEXT |
不限长度的大文本,适合存储正文、评论等长内容。例如:文章内容、评论内容
|
SlugField |
VARCHAR(n) |
URL 友好的短字符串,仅允许英文字母、数字、连字符。例如:博客文章的 URL slug
|
EmailField |
VARCHAR(n) |
自动校验格式的 Email 字符串。例如:用户注册邮箱
|
URLField |
VARCHAR(n) |
自动校验格式的 URL 字符串。例如:个人主页链接
|
UUIDField |
CHAR(32) |
存储 UUID 值,常用于唯一标识,如主键。例如:订单编号、用户唯一ID
|
FilePathField |
VARCHAR(n) |
文件路径字段,从指定目录选择文件。例如:静态日志路径选择
|
数值类型字段
字段类型 | SQL 类型 | 说明 |
---|---|---|
IntegerField |
INT |
整数类型(存储范围:-2147483648 到 2147483647),适合年龄、数量等常规场景。例如:用户年龄
|
SmallIntegerField |
SMALLINT |
小范围整数(存储范围:-32768 到 32767,节省存储空间)。例如:积分等级、等级标识
|
PositiveIntegerField |
INT UNSIGNED |
非负整数,不允许为负。例如:商品库存数量
|
PositiveSmallIntegerField |
SMALLINT UNSIGNED |
小范围非负整数。例如:星级评分(1~5)
|
BigIntegerField |
BIGINT |
适合较大的整数(存储范围:-9223372036854775808 到 9223372036854775807),如访问量、金额(单位:分)。例如:浏览量统计
|
FloatField |
FLOAT |
存储浮点数,精度一般。例如:商品折扣、体重
|
DecimalField |
DECIMAL(m, d) |
高精度小数,适合金额。需指定
|
布尔类型字段
字段类型 | SQL 类型 | 说明 |
---|---|---|
BooleanField |
TINYINT(1) |
布尔值字段,只允许 True/False。例如:是否启用账号is_active = models.BooleanField(default=True) |
NullBooleanField (已废弃) |
TINYINT(1) |
可为 True/False/NULL,推荐改用 BooleanField(null=True) 。例如:是否已验证,允许空值(旧代码中使用) |
时间/日期类型字段
字段类型 | SQL 类型 | 说明 |
---|---|---|
DateField |
DATE |
日期字段,存储年月日,不含时间。例如:用户生日
|
TimeField |
TIME |
时间字段,仅包含时分秒。例如:商店营业时间
|
DateTimeField |
DATETIME |
日期+时间,常用于记录时间戳。例如:创建时间、更新时间
|
DurationField |
BIGINT (秒) |
表示时间间隔(Python
|
关系字段(模型之间的关系)
字段类型 | SQL 类型 | 说明 |
---|---|---|
ForeignKey |
INT + 外键 |
一对多关系,当前模型是“多”那一方。例如:一本书属于一个作者author = models.ForeignKey(Author, on_delete=models.CASCADE) |
OneToOneField |
INT + 唯一 |
一对一关系,每个对象都唯一绑定另一个对象。例如:一个用户对应一个身份证信息id_card = models.OneToOneField(IDCard, on_delete=models.CASCADE) |
ManyToManyField |
中间表 | 多对多关系,自动生成中间表。例如:学生选课,一个学生可选多门课courses = models.ManyToManyField(Course) |
文件和图片上传字段
字段类型 | SQL 类型 | 说明 |
---|---|---|
FileField |
VARCHAR(100) |
用于上传文件,必须设置 upload_to 。例如:合同上传、附件提交attachment = models.FileField(upload_to='uploads/') |
ImageField |
VARCHAR(100) |
上传图片,继承自 FileField ,需安装 Pillow 库。例如:用户头像、商品主图avatar = models.ImageField(upload_to='avatars/') |
特殊类型字段
字段类型 | SQL 类型 | 说明 |
---|---|---|
GenericIPAddressField |
VARCHAR(39) |
存储 IP 地址(IPv4 或 IPv6)。例如:登录 IP 记录ip_address = models.GenericIPAddressField() |
JSONField |
JSON (MySQL 5.7+) |
存储结构化 JSON 数据。支持字典、列表等对象。例如:动态配置项、表单结构settings = models.JSONField(default=dict) |
字段参数
通用字段参数
适用于大多数字段类型(如 CharField
、IntegerField
、BooleanField
、DateTimeField
等),可用于控制数据库行为、表单验证、管理后台展示等。
参数名 | 说明 | 使用场景 |
---|---|---|
null |
允许数据库字段为 NULL (空),默认为False |
比如“用户地址”、“头像”不一定填写,设置 null=True |
blank |
允许表单验证时为空(用于 admin 和表单),默认为False | “备注”、“个人简介”这类字段,设置 blank=True |
default |
设置默认值 若字段未设置 default且未设置null=True ,则必须显式赋值,否则保存时会报错若设置了 default ,即使未设置null=True 也能自动填充默认值 |
比如 is_active = models.BooleanField(default=True) ,用户默认启用 |
choices |
choices 是一个二元组列表或元组,格式为 (数据库存储值, 人类可读值) ,用于限制该字段的可选值多数字段支持(特别是字符串和整数) |
设置选项元组(或枚举)性别字段:choices=[('M', '男'), ('F', '女')] |
unique |
设置字段唯一性约束 | 用户名、邮箱、身份证号等需唯一,设为 unique=True |
db_index |
为字段建立数据库索引,加快查询 | 高频查询字段如手机号、订单号建议加索引 |
editable |
设置字段是否可在 admin 界面或表单中编辑 | 如 created_at = models.DateTimeField(auto_now_add=True, editable=False) |
verbose_name |
设置后台显示的字段名称 | 改善 admin 可读性,如 email = models.EmailField(verbose_name="电子邮箱") |
help_text |
设置后台或表单中的提示信息 | help_text="请输入真实姓名,最多50个字符" |
validators |
添加自定义验证器函数或类 | 限制范围、匹配格式,如电话号码、价格范围验证 |
error_messages |
自定义错误提示 | 表单验证失败时,提示更友好 |
db_column |
自定义字段在数据库中的列名 | 数据库字段名需要特殊命名时使用,如旧数据库兼容 |
primary_key |
设置字段为主键 | 自定义主键如 UUID,一般使用 id = models.UUIDField(primary_key=True) |
字符串字段专用
参数 | 适用字段 | 说明 | 使用场景 |
---|---|---|---|
max_length |
CharField , EmailField , SlugField 等 |
设置最大字符数 | 用户名最多20字符,邮箱最多100字符等 |
数值字段专用
参数名 | 适用字段 | 说明 | 使用场景示例 |
---|---|---|---|
max_digits |
DecimalField |
指定数值总共最多可以有多少位(包括整数位和小数位) | 商品价格字段,最大支持 99999999.99 → max_digits=10 |
decimal_places |
DecimalField |
指定小数点后保留几位 | 金额统一保留两位 → decimal_places=2 |
auto_created |
所有数值字段(自动生成时) | 通常由 ORM 内部使用,用于自动生成字段标记(开发中无需设置) | Django 自动生成 through 模型中的字段会包含该属性(不建议手动设置) |
时间日期字段专用
参数 | 适用字段 | 说明 | 使用场景 |
---|---|---|---|
auto_now_add |
DateTimeField , DateField |
创建记录时自动填当前时间 | created_at 创建时间戳 |
auto_now |
DateTimeField , DateField |
每次保存自动更新时间 | updated_at 更新时间戳 |
关系字段专用
参数名 | 适用字段 | 说明 | 使用场景(开发示例) |
---|---|---|---|
to |
所有关系字段 | 要关联的目标模型,可以是模型类或 'app.ModelName' 字符串形式 |
ForeignKey('auth.User') 表示关联用户表,跨 app 时常用 |
on_delete |
ForeignKey , OneToOneField |
被关联对象删除时的处理行为,必须设置 | 删除用户时是否同时删除其文章,可用 CASCADE 或 SET_NULL 等策略 |
related_name |
所有关系字段 | 反向查询的管理器名 | 设置为 articles ,可用 user.articles.all() 获取该用户文章 |
related_query_name |
所有关系字段 | 反向查询中的过滤名(用于 .filter() ) |
允许使用 User.objects.filter(articles__title__icontains='Python') |
limit_choices_to |
所有关系字段 | 限制外键选择范围,可传 dict 或 Q 对象 |
仅允许选择 is_active=True 的用户作为客服负责人 |
to_field |
所有关系字段 | 关联到目标模型的哪个字段,默认是主键 | 如用用户名建立关联:to_field='username' |
db_constraint |
所有关系字段 | 是否在数据库中添加外键约束 | 某些场景希望逻辑关联但不加数据库约束,如异步同步外部数据 |
swappable |
所有关系字段 | 是否允许替换模型引用,常用于 AUTH_USER_MODEL |
用于 ForeignKey(settings.AUTH_USER_MODEL, swappable=True) |
symmetrical |
ManyToManyField (自关联时) |
自关联是否为对称关系,默认是 True |
朋友关系:A加了B,B也自动加了A;关注则设置为 False |
through |
ManyToManyField |
指定中间表模型以扩展多对多关系 | 学生选课记录需要额外存储成绩、时间等中间字段 |
through_fields |
ManyToManyField |
指定中间模型中的源字段和目标字段 | 中间模型字段不是默认命名时需要指定,如 ('student', 'course') |
附:on_delete
常用选项
选项 | 含义 | 应用场景 |
---|---|---|
models.CASCADE |
级联删除:删除主对象时连带删除当前对象 | 订单删除 → 同时删除订单项 |
models.PROTECT |
拒绝删除主对象(抛出 ProtectedError ) |
用户有发布记录,不允许直接删用户 |
models.SET_NULL |
置为 NULL(需要设置 null=True ) |
文章作者删除后,保留文章但作者字段为空 |
models.SET_DEFAULT |
设为默认值(需设置 default= ) |
作者删了 → 改为匿名用户 |
models.SET(...) |
设置为指定值或函数返回值 | on_delete=models.SET(get_deleted_user) |
models.DO_NOTHING |
什么都不做(数据库层面可能报错) | 不推荐使用 |
文件上传字段专用
适用于FileField
, ImageField字段
参数 | 说明 | 使用场景 |
---|---|---|
upload_to |
设置上传路径(字符串或函数) | upload_to="uploads/%Y/%m/%d" :按日期分目录存储上传图像 |
storage |
设置自定义文件存储类 | 使用阿里云 OSS、AWS S3 等云存储时使用 |
模型字段小技巧与注意事项
技巧 | 说明 | 代码示例 |
---|---|---|
1. 区分 null=True 与 blank=True |
null=True 控制数据库层面是否允许存储 NULL。blank=True 控制 Django 表单层面是否允许空值。开发中,可选字段通常两个都设置。 |
|
2. 自动时间字段推荐用法 |
|
|
3. 高频查询字段加索引 | 使用 db_index=True ,能显著提升过滤、查询效率。适合手机号、邮箱、订单号等高频查询字段。 |
|
4. 使用 choices 枚举提升可维护性 |
Django 3.x+ 支持 TextChoices 和 IntegerChoices ,方便定义枚举。枚举值和显示文字分离,代码更清晰,方便后台表单选择。 |
|
5. 布尔字段不要用 null=True |
布尔字段有三态(True、False、NULL)容易引起逻辑错误。 推荐使用 |
|
6. upload_to 支持函数实现动态路径 |
upload_to 可以传入函数,动态生成路径(按用户ID、时间分目录等)。 |
|
7. 避免在模型中使用可变默认参数 | 字典、列表等可变对象作为默认值会被所有实例共享,导致数据混乱。 推荐使用函数返回默认值。 |
|
8. 使用 unique_together 或 UniqueConstraint 实现复合唯一 |
复合唯一保证多个字段组合唯一,防止重复数据。 |
|
9. 自定义 verbose_name 和 help_text 改善后台体验 |
自定义 verbose_name 和 help_text 优化后台体验 |
|
10. 使用 validators 做自定义验证 |
自定义或内置验证器可以限制格式、范围等,增强数据准确性。 |
|
5. 模型(Model)创建示例
下面是一个完整的博客系统模型创建示例,包含各种常用字段类型和关系字段:
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
from django.urls import reverse
class Category(models.Model):
"""
博客分类模型
"""
name = models.CharField('分类名称', max_length=100)
created = models.DateTimeField('创建时间', auto_now_add=True)
class Meta:
db_table = 'category'
verbose_name = '分类'
verbose_name_plural = verbose_name
ordering = ['-created']
def __str__(self):
return self.name
class Tag(models.Model):
"""
博客标签模型
"""
name = models.CharField('标签名称', max_length=100)
created = models.DateTimeField('创建时间', auto_now_add=True)
class Meta:
db_table = 'tag'
verbose_name = '标签'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
class Post(models.Model):
"""
博客文章模型
"""
# 文章状态选项
STATUS_CHOICES = (
('draft', '草稿'),
('published', '已发布'),
)
# 基础字段
title = models.CharField('标题', max_length=200)
slug = models.SlugField('URL别名', max_length=200, unique_for_date='publish')
author = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='blog_posts',
verbose_name='作者'
)
body = models.TextField('正文内容')
publish = models.DateTimeField('发布时间', default=timezone.now)
created = models.DateTimeField('创建时间', auto_now_add=True)
updated = models.DateTimeField('更新时间', auto_now=True)
status = models.CharField(
'文章状态',
max_length=10,
choices=STATUS_CHOICES,
default='draft'
)
# 关系字段
category = models.ForeignKey(
Category,
on_delete=models.CASCADE,
related_name='posts',
verbose_name='分类'
)
tags = models.ManyToManyField(
Tag,
related_name='posts',
blank=True,
verbose_name='标签'
)
# 数字字段
views = models.PositiveIntegerField('浏览量', default=0)
likes = models.PositiveIntegerField('点赞数', default=0)
# 布尔字段
is_top = models.BooleanField('置顶文章', default=False)
is_recommend = models.BooleanField('推荐文章', default=False)
# 文件字段
cover_image = models.ImageField(
'封面图片',
upload_to='posts/%Y/%m/%d/',
blank=True
)
attachment = models.FileField(
'附件',
upload_to='attachments/%Y/%m/%d/',
blank=True
)
class Meta:
db_table = 'post'
verbose_name = '文章'
verbose_name_plural = verbose_name
ordering = ('-publish',)
indexes = [
models.Index(fields=['-publish']),
]
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blog:post_detail', args=[
self.publish.year,
self.publish.month,
self.publish.day,
self.slug
])
def increase_views(self):
self.views += 1
self.save(update_fields=['views'])
class Comment(models.Model):
"""
文章评论模型
"""
post = models.ForeignKey(
Post,
on_delete=models.CASCADE,
related_name='comments',
verbose_name='所属文章'
)
name = models.CharField('评论者', max_length=80)
email = models.EmailField('邮箱')
body = models.TextField('评论内容')
created = models.DateTimeField('创建时间', auto_now_add=True)
updated = models.DateTimeField('更新时间', auto_now=True)
active = models.BooleanField('是否显示', default=True)
# 自引用多对多关系,用于实现评论回复
parent = models.ForeignKey(
'self',
null=True,
blank=True,
on_delete=models.CASCADE,
related_name='replies',
verbose_name='父评论'
)
class Meta:
db_table = 'comment'
verbose_name = '评论'
verbose_name_plural = verbose_name
ordering = ('created',)
def __str__(self):
return f'由 {self.name} 对 {self.post} 的评论'
一对多关系 (ForeignKey)
Post 与 Category 的关系
category = models.ForeignKey(
Category,
on_delete=models.CASCADE,
related_name='posts',
verbose_name='分类'
)
关系解析:
一个分类(Category)可以对应多篇文章(Post)
一篇文章(Post)只能属于一个分类(Category)
on_delete=models.CASCADE
表示当分类被删除时,属于该分类的所有文章也会被删除related_name='posts'
允许通过分类实例访问其所有文章:category.posts.all()
SQL表现:
在Post表中会有一个
category_id
字段存储关联的Category主键
Post 与 User 的关系
author = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='blog_posts',
verbose_name='作者'
)
关系解析:
一个用户(User)可以写多篇文章(Post)
一篇文章(Post)只能有一个作者(User)
related_name='blog_posts'
允许通过用户实例访问其所有文章:user.blog_posts.all()
多对多关系 (ManyToManyField)
Post 与 Tag 的关系
tags = models.ManyToManyField(
Tag,
related_name='posts',
blank=True,
verbose_name='标签'
)
关系解析:
一篇文章(Post)可以有多个标签(Tag)
一个标签(Tag)可以标记多篇文章(Post)
blank=True
表示文章可以不关联任何标签related_name='posts'
允许通过标签实例访问关联的所有文章:tag.posts.all()
SQL表现:
Django会自动创建一个中间表来维护这种多对多关系,格式为:
模型名称_字段名称,此处生成的中间表名为:
post_tags
自引用关系 (自关联)
Comment 的自引用关系
parent = models.ForeignKey(
'self',
null=True,
blank=True,
on_delete=models.CASCADE,
related_name='replies',
verbose_name='父评论'
)
关系解析:
一条评论(Comment)可以回复另一条评论(形成评论树)
null=True, blank=True
表示评论可以不回复任何其他评论(顶级评论)related_name='replies'
允许通过评论实例访问其所有回复:comment.replies.all()
on_delete=models.CASCADE
表示当父评论被删除时,其所有回复也会被删除
反向关系查询示例
Django 的反向查询让你可以从被关联的对象出发,反查所有引用它的对象,实现数据关系的双向访问,提高开发效率和代码可读性,是 ORM 系统中非常关键的一部分。
正向关系和反向关系
正向关系:从"多"的一方找"一"的一方(如:文章找作者)
反向关系:从"一"的一方找"多"的一方(如:作者找所有文章)
Django自动创建的反向查询名默认是
模型名小写_set
,但可以用related_name
改名
就像快递站里:
正向关系是"通过快递找主人"→ 快递
.主人
就能找到你(这是直观的)反向关系是"通过主人找所有快递" → 主人知道他618买的几十个快递在哪,主人
.快递
就能找到所有快递(这就是反向关系)
示例
用户(Author)与文章(Post) - 一对多
正向查询:从文章找作者
post = Post.objects.get(id=1)
author = post.author # 获取这篇文章的作者
反向查询:从用户找他写的所有文章
user = User.objects.get(username='张三')
user_posts = user.blog_posts.all() # 获取张三写的所有文章
# 如果没有定义related_name,则是 user.post_set.all()
标签(Tag)与文章(Post) - 多对多
正向查询:从文章找所有标签
post = Post.objects.get(id=1)
tags = post.tags.all() # 获取这篇文章的所有标签
反向查询:从标签找所有关联的文章
tag = Tag.objects.get(name='Django')
django_posts = tag.posts.all() # 获取所有标记为Django的文章