当前位置: 首页 > news >正文

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 类型说明
CharFieldVARCHAR(n)

可控长度的短文本,必须指定 max_length。例如:用户名、标题等

username = models.CharField(max_length=150)

TextFieldLONGTEXT

不限长度的大文本,适合存储正文、评论等长内容。例如:文章内容、评论内容

content = models.TextField()

SlugFieldVARCHAR(n)

URL 友好的短字符串,仅允许英文字母、数字、连字符。例如:博客文章的 URL slug

slug = models.SlugField(max_length=100)

EmailFieldVARCHAR(n)

自动校验格式的 Email 字符串。例如:用户注册邮箱

email = models.EmailField(max_length=255)

URLFieldVARCHAR(n)

自动校验格式的 URL 字符串。例如:个人主页链接

website = models.URLField()

UUIDFieldCHAR(32)

存储 UUID 值,常用于唯一标识,如主键。例如:订单编号、用户唯一ID

uuid = models.UUIDField(default=uuid.uuid4, editable=False)

FilePathFieldVARCHAR(n)

文件路径字段,从指定目录选择文件。例如:静态日志路径选择

log_file = models.FilePathField(path="/var/logs/")

数值类型字段 

字段类型SQL 类型说明
IntegerFieldINT

整数类型(存储范围:-2147483648 到 2147483647),适合年龄、数量等常规场景。例如:用户年龄

age = models.IntegerField()

SmallIntegerFieldSMALLINT

小范围整数(存储范围:-32768 到 32767,节省存储空间)。例如:积分等级、等级标识

level = models.SmallIntegerField()

PositiveIntegerFieldINT UNSIGNED

非负整数,不允许为负。例如:商品库存数量

stock = models.PositiveIntegerField()

PositiveSmallIntegerFieldSMALLINT UNSIGNED

小范围非负整数。例如:星级评分(1~5)

rating = models.PositiveSmallIntegerField()

BigIntegerFieldBIGINT

适合较大的整数(存储范围:-9223372036854775808 到 9223372036854775807),如访问量、金额(单位:分)。例如:浏览量统计

views = models.BigIntegerField()

FloatFieldFLOAT

存储浮点数,精度一般。例如:商品折扣、体重

discount = models.FloatField()

DecimalFieldDECIMAL(m, d)

高精度小数,适合金额。需指定 max_digitsdecimal_places。例如:订单金额

price = models.DecimalField(max_digits=10, decimal_places=2)

布尔类型字段 

字段类型SQL 类型说明
BooleanFieldTINYINT(1)布尔值字段,只允许 True/False。例如:是否启用账号is_active = models.BooleanField(default=True)
NullBooleanField(已废弃)TINYINT(1)可为 True/False/NULL,推荐改用 BooleanField(null=True)。例如:是否已验证,允许空值(旧代码中使用)

时间/日期类型字段 

字段类型SQL 类型说明
DateFieldDATE

日期字段,存储年月日,不含时间。例如:用户生日

birth_date = models.DateField(null=True)

TimeFieldTIME

时间字段,仅包含时分秒。例如:商店营业时间

open_time = models.TimeField(null=True)

DateTimeFieldDATETIME

日期+时间,常用于记录时间戳。例如:创建时间、更新时间

created_at = models.DateTimeField(auto_now_add=True)

DurationFieldBIGINT(秒)

表示时间间隔(Python timedelta 对象)。例如:视频播放时长

duration = models.DurationField()

关系字段(模型之间的关系) 

字段类型SQL 类型说明
ForeignKeyINT + 外键一对多关系,当前模型是“多”那一方。例如:一本书属于一个作者author = models.ForeignKey(Author, on_delete=models.CASCADE)
OneToOneFieldINT + 唯一一对一关系,每个对象都唯一绑定另一个对象。例如:一个用户对应一个身份证信息id_card = models.OneToOneField(IDCard, on_delete=models.CASCADE)
ManyToManyField中间表多对多关系,自动生成中间表。例如:学生选课,一个学生可选多门课courses = models.ManyToManyField(Course)

文件和图片上传字段 

字段类型SQL 类型说明
FileFieldVARCHAR(100)用于上传文件,必须设置 upload_to。例如:合同上传、附件提交attachment = models.FileField(upload_to='uploads/')
ImageFieldVARCHAR(100)上传图片,继承自 FileField,需安装 Pillow 库。例如:用户头像、商品主图avatar = models.ImageField(upload_to='avatars/')

特殊类型字段 

字段类型SQL 类型说明
GenericIPAddressFieldVARCHAR(39)存储 IP 地址(IPv4 或 IPv6)。例如:登录 IP 记录ip_address = models.GenericIPAddressField()
JSONFieldJSON(MySQL 5.7+)存储结构化 JSON 数据。支持字典、列表等对象。例如:动态配置项、表单结构settings = models.JSONField(default=dict)

字段参数

通用字段参数

适用于大多数字段类型(如 CharFieldIntegerFieldBooleanFieldDateTimeField 等),可用于控制数据库行为、表单验证、管理后台展示等。

参数名说明使用场景
null允许数据库字段为 NULL(空),默认为False比如“用户地址”、“头像”不一定填写,设置 null=True
blank允许表单验证时为空(用于 admin 和表单),默认为False“备注”、“个人简介”这类字段,设置 blank=True
default设置默认值
若字段未设置 default且未设置null=True,则必须显式赋值,否则保存时会报错
若设置了 default,即使未设置null=True 也能自动填充默认值
比如 is_active = models.BooleanField(default=True),用户默认启用
choiceschoices 是一个二元组列表或元组,格式为 (数据库存储值, 人类可读值),用于限制该字段的可选值
多数字段支持(特别是字符串和整数)
设置选项元组(或枚举)性别字段: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_lengthCharField, EmailField, SlugField设置最大字符数用户名最多20字符,邮箱最多100字符等

数值字段专用

参数名适用字段说明使用场景示例
max_digitsDecimalField指定数值总共最多可以有多少位(包括整数位和小数位)商品价格字段,最大支持 99999999.99max_digits=10
decimal_placesDecimalField指定小数点后保留几位金额统一保留两位 → decimal_places=2
auto_created所有数值字段(自动生成时)通常由 ORM 内部使用,用于自动生成字段标记(开发中无需设置)Django 自动生成 through 模型中的字段会包含该属性(不建议手动设置)

时间日期字段专用 

参数适用字段说明使用场景
auto_now_addDateTimeField, DateField创建记录时自动填当前时间created_at 创建时间戳
auto_nowDateTimeField, DateField每次保存自动更新时间updated_at 更新时间戳

关系字段专用

参数名适用字段说明使用场景(开发示例)
to所有关系字段要关联的目标模型,可以是模型类或 'app.ModelName' 字符串形式ForeignKey('auth.User') 表示关联用户表,跨 app 时常用
on_deleteForeignKey, OneToOneField被关联对象删除时的处理行为,必须设置删除用户时是否同时删除其文章,可用 CASCADESET_NULL 等策略
related_name所有关系字段反向查询的管理器名设置为 articles,可用 user.articles.all() 获取该用户文章
related_query_name所有关系字段反向查询中的过滤名(用于 .filter()允许使用 User.objects.filter(articles__title__icontains='Python')
limit_choices_to所有关系字段限制外键选择范围,可传 dictQ 对象仅允许选择 is_active=True 的用户作为客服负责人
to_field所有关系字段关联到目标模型的哪个字段,默认是主键如用用户名建立关联:to_field='username'
db_constraint所有关系字段是否在数据库中添加外键约束某些场景希望逻辑关联但不加数据库约束,如异步同步外部数据
swappable所有关系字段是否允许替换模型引用,常用于 AUTH_USER_MODEL用于 ForeignKey(settings.AUTH_USER_MODEL, swappable=True)
symmetricalManyToManyField(自关联时)自关联是否为对称关系,默认是 True朋友关系:A加了B,B也自动加了A;关注则设置为 False
throughManyToManyField指定中间表模型以扩展多对多关系学生选课记录需要额外存储成绩、时间等中间字段
through_fieldsManyToManyField指定中间模型中的源字段和目标字段中间模型字段不是默认命名时需要指定,如 ('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=Trueblank=Truenull=True 控制数据库层面是否允许存储 NULL。
blank=True 控制 Django 表单层面是否允许空值。
开发中,可选字段通常两个都设置。
 
notes = models.TextField(null=True, blank=True)

2. 自动时间字段推荐用法

auto_now_add=True 用于记录创建时间,只会设置一次。

auto_now=True 用于记录更新时间,每次保存都会更新。
两者不要写在同一个字段上。

created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
3. 高频查询字段加索引使用 db_index=True,能显著提升过滤、查询效率。
适合手机号、邮箱、订单号等高频查询字段。

phone = models.CharField(max_length=20, db_index=True)
4. 使用 choices 枚举提升可维护性Django 3.x+ 支持 TextChoicesIntegerChoices,方便定义枚举。
枚举值和显示文字分离,代码更清晰,方便后台表单选择。
 
from django.db import modelsclass Status(models.TextChoices):PENDING = 'P', '待处理'PAID = 'D', '已支付'status = models.CharField(max_length=1, choices=Status.choices, default=Status.PENDING)
5. 布尔字段不要用 null=True

布尔字段有三态(True、False、NULL)容易引起逻辑错误。

推荐使用 default=True/False 避免空值。

 
is_active = models.BooleanField(default=True)
6. upload_to 支持函数实现动态路径upload_to 可以传入函数,动态生成路径(按用户ID、时间分目录等)。 
def user_directory_path(instance, filename):return f'user_{instance.user.id}/{filename}'avatar = models.ImageField(upload_to=user_directory_path)
7. 避免在模型中使用可变默认参数

字典、列表等可变对象作为默认值会被所有实例共享,导致数据混乱。

推荐使用函数返回默认值。

 
def default_settings():return {}settings = models.JSONField(default=default_settings)
8. 使用 unique_togetherUniqueConstraint 实现复合唯一复合唯一保证多个字段组合唯一,防止重复数据。 
class Meta:unique_together = [('user', 'date')]
9. 自定义 verbose_namehelp_text 改善后台体验自定义 verbose_namehelp_text 优化后台体验 
email = models.EmailField(verbose_name='电子邮箱', help_text='请输入有效邮箱地址')
10. 使用 validators 做自定义验证自定义或内置验证器可以限制格式、范围等,增强数据准确性。 
from django.core.validators import RegexValidatorphone_validator = RegexValidator(r'^\d{10}$', '手机号格式错误')phone = models.CharField(validators=[phone_validator], max_length=10)

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的文章

http://www.lqws.cn/news/539785.html

相关文章:

  • 安全运营中的漏洞管理和相关KPI
  • 桌面小屏幕实战课程:DesktopScreen 13 HTTP SERVER
  • PHP Protobuf 手写生成器,
  • BERT架构详解
  • 智能温差发电杯(项目计划书)
  • LinuxBridge的作用与发展历程:从基础桥接到云原生网络基石
  • AIOps与人工智能的融合:从智能运维到自适应IT生态的革命
  • 【Linux指南】压缩、网络传输与系统工具
  • webGL面试题200道
  • Vue3 + Element Plus Transfer 穿梭框自定义分组
  • 【docker】构建时使用宿主机的代理
  • HarmonyOS NEXT仓颉开发语言实战案例:简约音乐播放页
  • jvm简单八股
  • model训练中python基本数据类型的保存输出
  • 爬虫006----Scrapy框架
  • 2025-6-27-C++ 学习 模拟与高精度(7)
  • Kotlin中协程挂起函数的本质
  • SpringBoot -- 整合Junit
  • 分布式session解决方案
  • 笔记:使用EasyExcel导入csv文件出现编码问题,导致导入数据全为null的解决方法
  • Apache Kafka 面试应答指南
  • 那些不应该的优化
  • html配置rem实现页面自适应
  • Linux:从后往前查看日志命令
  • 编译原理---文法和语法分析
  • 基于全局构建版本和ES模块构建版本的vue3 快速上手
  • LLM驱动开发:正在重塑软件工程的下一场革命
  • Maven生命周期与阶段扩展深度解析
  • GO 语言学习 之 语句块
  • vscode把less文件生成css文件配置,设置生成自定义文件名称和路径