Django ORM 1. 创建模型(Model)
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 pymysqlpymysql.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="商品名称") # 字符串字段,限制最大长度为 100price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="价格") # 精确的金额字段,最大 10 位数,小数点后保留 2 位stock = models.IntegerField(default=0, verbose_name="库存数量") # 库存数量,整型,默认值为 0is_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.nameclass Meta:db_table = 'product' # 自定义表名,默认表名是 web_product,现在指定为 productordering = ['-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 reverseclass 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_nameordering = ['-created']def __str__(self):return self.nameclass 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_namedef __str__(self):return self.nameclass 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_nameordering = ('-publish',)indexes = [models.Index(fields=['-publish']),]def __str__(self):return self.titledef 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 += 1self.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_nameordering = ('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的文章