Django 模型与 ORM 全解析(一):从基础到实战的完整指南
前面已经通过两篇文章,带大家了解了Django第一个项目的环境的搭建和相关概念,还带大家了解了settings中的一些基本的常用的配置。了解到了Django使用的MTV 架构:
模型(Model):处于 MTV 模式底层,是数据存取层(数据库)。
模板(Template):位于 MTV 模式顶层,是表现层(前端逻辑代码)。
视图(View):处于 MTV 模式中间层,是业务逻辑层(后端页面)。
URL 分发器:虽未在 MTV 名称中体现,但至关重要,相当于 MVC 模式中的控制器(Controller)。它使用正则表达式匹配 URL,将 URL 页面请求分发给不同的视图处理,再由视图调用相应的模型和模板。
因为前端的可以使用HTML和Vue等技术来实现,所以接下来主要是和大家介绍视图层(View)以及模型(Model),这篇文章小宁带大家一起了解一下模型层(Model)。
一、引言
1.1 Django “数据驱动” 开发的核心逻辑
在 Django 开发中,所有业务功能最终都围绕 “数据” 展开 —— 用户信息、商品数据、文章内容等都需要存储和管理。而模型(Model)正是 Django 对数据库表的抽象封装,ORM(对象关系映射)则作为中间层,让开发者无需编写复杂 SQL,直接用 Python 代码操作数据库,极大降低了数据库操作的门槛。
1.2 ORM 如何简化数据库操作
传统开发中,操作 MySQL 需要编写SELECT * FROM table WHERE ...
这类 SQL 语句,且不同数据库(如 MySQL、PostgreSQL)的 SQL 语法存在差异。Django ORM 则实现了 “Python 对象→数据库表” 的自动映射:定义一个 Python 类(模型)即对应一张数据库表,调用Model.objects.all()
即可获取所有数据,无需关注底层 SQL 实现,还能兼容多种数据库。
二、Django 模型基础
2.1 认识 Django 模型
2.1.1 模型的本质
Django 模型是继承自django.db.models.Model
的 Python 类,它并非直接操作数据库,而是通过类的结构 “描述” 数据库表的结构 —— 类名对应表名,类的属性对应表的字段,类的实例对应表中的一条记录。
2.1.2 模型的核心作用
模型的核心价值体现在四方面:
- 定义数据结构:明确字段类型(如字符串、日期)和约束(如最大长度、是否唯一);
- 提供操作接口:通过
objects
管理器提供增删改查(CRUD)方法; - 实现数据验证:自动校验字段格式(如
EmailField
校验邮箱格式); - 处理数据库关系:支持一对多、一对一、多对多等关联关系。
2.1.3 模型与数据库的映射关系
映射关系是理解模型的关键,以 “图书(Book)” 模型为例:
Django 模型概念 | 数据库概念 | 示例 |
---|---|---|
模型类(Book) | 数据库表 | book 表 |
类属性(title) | 表字段 | title 字段(VARCHAR 类型) |
类实例(book = Book (...)) | 表记录 | book 表中一条包含标题、作者的记录 |
2.2 模型与 ORM
2.2.1 什么是 ORM?
ORM 是一种程序设计技术,它将面向对象编程中的 “对象” 与关系型数据库中的 “表” 关联起来,通过元数据(如模型的字段定义)实现两者之间的自动转换,让开发者用操作对象的方式操作数据库。
2.2.2 Django ORM 的核心优势
Django ORM 的优势可总结为三点:
- 跨数据库兼容:同一套 Python 代码可操作 MySQL、PostgreSQL、SQLite 等,无需修改语法;
- 自动生成 SQL:调用
Model.objects.filter(...)
时,ORM 会根据数据库类型自动生成对应的 SQL 语句; - 降低学习成本:无需深入学习 SQL,掌握 Python 基础即可操作数据库。
2.2.3 Django 模型与 ORM 的工作流程
以 “创建图书记录” 为例,流程如下:
- 开发者编写
Book.objects.create(title="Django 5教程", author="张三")
; - ORM 层解析该 Python 代码,生成 MySQL 语句
INSERT INTO book (title, author) VALUES ("Django 5教程", "张三")
; - ORM 将 SQL 语句发送给 MySQL 数据库执行,完成数据插入;
2.3 模型与 MySQL
2.3.1 安装 MySQL 驱动
Django 操作 MySQL 需要依赖 Python 驱动,常用两种选择:
pymysql
:纯 Python 实现,安装简单,适合开发环境,命令:pip install pymysql
;
# 使用清华大学镜像源
pip install PyMySQL -i https://pypi.tuna.tsinghua.edu.cn/simple/
mysqlclient
:C 语言实现,性能更优,适合生产环境,命令:pip install mysqlclient
(若安装失败,需先安装 MySQL 开发依赖,如 Ubuntu 的libmysqlclient-dev
)。
pip install mysqlclient
2.3.2 配置 settings.py
在 Django 项目的settings.py
中,通过DATABASES
配置 MySQL 连接信息,示例如下(文档中提供的标准配置):
DATABASES = {'default':{'ENGINE': 'django.db.backends.mysql', # 数据库引擎为MySQL'NAME': 'csdn', # 数据库名称'USER': 'root', # 数据库⽤户名'PASSWORD': '123456', # 数据库密码'HOST': '127.0.0.1', # 数据库服务器地址'PORT': '3306', # 数据库端⼝号}
}
2.3.3 避坑指南
- 错误 1:
ModuleNotFoundError: No module named 'MySQLdb'
:未安装驱动,安装pymysql
或mysqlclient
即可; 错误 2:
Access denied for user 'myuser'@'localhost'
:用户名或密码错误,检查USER
和PASSWORD
参数;- 错误 3:
Unknown database 'mydatabase'
:数据库未创建,需先在 MySQL 中执行CREATE DATABASE mydatabase;
。
2.4 定义第一个 Django 模型
2.4.1 模型定义的位置
Django 模型必须定义在 “应用(App)” 的models.py
中 —— 每个 App 对应一个业务模块(如 “图书管理” App),模型则是该模块的数据结构描述。若还未创建 App,需先执行python manage.py startapp myapp
。
2.4.2 基础模型案例
Book
模型定义,包含图书标题、作者、出版日期、价格等核心字段,代码如下:
from django.db import modelsclass Book(models.Model):"""Book模型类,用于表示图书信息"""# 图书标题:字符串类型,最大长度200title = models.CharField(max_length=200)# 作者:字符串类型,最大长度100author = models.CharField(max_length=100)# 出版日期:日期类型publication_date = models.DateField()# 价格:十进制类型,最多5位数字,小数点后2位(如99.99)price = models.DecimalField(max_digits=5, decimal_places=2)def __str__(self):"""返回图书标题作为对象的字符串表示(在admin界面显示)"""return self.title
2.4.3 模型使用的完整流程
定义模型后,需经过 5 步(定义→生成迁移→应用迁移→视图 / 模板调用)才能正常使用,每一步均来自文档的标准流程:
1. 创建迁移文件:Django 根据模型生成数据库表结构的 “蓝图”,命令:
前提:配置好 “2.3.2 配置 settings.py”的数据库配置,然后建立好自己的表。
python manage.py makemigrations myapp # myapp为App名称
执行后会在myapp/migrations
目录下生成迁移文件(如0001_initial.py
)。
2. 应用迁移:将迁移文件对应的表结构创建到数据库中,命令:
python manage.py migrate myapp
执行后,MySQL 中会自动创建book
表(表名默认是 “App 名_模型名小写”,如myapp_book
)。
3.在视图中使用模型:在myapp/views.py
中查询模型数据,传递给模板:
from django.shortcuts import render
from .models import Bookdef book_list(request):books = Book.objects.all() # 获取所有图书数据return render(request, 'books/list.html', {'books': books}) # 传递给模板
4. 在模板中使用模型数据:在templates/books/list.html
中渲染数据,文档中的示例如下:
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>图书列表</title>
</head>
<body><h1>图书列表</h1>{% if books %}{% for book in books %}<div class="book"><div class="book-title">{{ book.title }}</div><div class="book-author">作者: {{ book.author }}</div><div class="book-price">价格: ¥{{ book.price }}</div>{% if book.publication_date %}<div>出版日期: {{ book.publication_date }}</div>{% endif %}</div>{% endfor %}{% else %}<p>暂无图书信息。</p>{% endif %}
</body>
</html>
5. 配置 URL 路由:在myapp/urls.py和主应用url.py
中定义路由,关联视图函数:
from django.urls import path, include
urlpatterns = [path('smyapp1/',include('Smyapp1.urls')),]
from django.urls import path
from . import viewsurlpatterns = [path('books/', views.book_list, name='book_list'), # 图书列表路由
]
测试:
①在数据库中添加数据
②在浏览器中输入:http://127.0.0.1:8000/smyapp1/books/
三、模型的核心组件:字段、关系与元数据
3.1 字段类型
3.1.1 常用字段类型详解
Django 提供多种字段类型,对应数据库的不同数据类型,文档中重点介绍的常用类型如下:
其中,DateTimeField
的两个常用参数需注意:
auto_now_add=True
:创建记录时自动填充当前时间(仅一次);auto_now=True
:每次保存记录时自动更新为当前时间(如更新时间)。
3.1.2 字段类型选择原则
选择字段类型时需遵循 “最小必要” 原则:
- 短文本用
CharField
(如用户名,长度固定),长文本用TextField
(如文章内容,长度不确定); - 金额类数据必须用
DecimalField
(避免FloatField
的浮点误差); - 时间类数据根据是否需要 “时分秒” 选择
DateField
或DateTimeField
。
3.2 字段约束:
3.2.1 核心字段约束
字段选项用于配置字段的约束和默认行为,本文中重点介绍的核心选项如下:
3.2.2 字段选项的常见误区
很多开发者混淆null
和blank
,两者的核心区别在于:
null=True
:针对数据库 —— 允许字段在数据库中存储为 NULL 值;blank=True
:针对表单 —— 允许表单提交时该字段为空(不影响数据库存储)。
正确搭配示例:- 可选的文本字段(如 “个人简介”):
bio = models.TextField(null=True, blank=True)
(数据库可存 NULL,表单可空); - 必选但允许空字符串的字段(如 “备注”):
remark = models.CharField(max_length=200, blank=True, null=False)
(表单可空,数据库存空字符串而非 NULL)。
3.3 关联关系字段
数据库表之间常存在关联关系(一对多、一对一、多对多),Django 通过专门的字段类型实现这些关系,文档中详细介绍了三种核心关联类型。
3.3.1 一对多关系:ForeignKey(外键)
1.定义语法(文档示例):
# 父模型:作者(一个作者对应多本书)
class Author(models.Model):name = models.CharField(max_length=100)email = models.EmailField()def __str__(self):return self.name# 子模型:图书(多本书对应一个作者)
class Book(models.Model):title = models.CharField(max_length=200)# 外键关联Author,on_delete指定关联删除规则author = models.ForeignKey(Author, on_delete=models.CASCADE)publication_date = models.DateField()def __str__(self):return self.title
2.on_delete 选项详解:on_delete
用于指定 “当父表记录被删除时,子表关联记录的处理方式”,文档中重点介绍的选项如下:
on_delete 值 | 作用 | 适用场景 |
---|---|---|
CASCADE | 级联删除:删除父记录时,子记录也删除 | 作者删除时,其所有图书也删除 |
PROTECT | 阻止删除:若有子记录,父记录无法删除 | 班级删除时,若有学生则不允许删除 |
SET_NULL | 设为 NULL:删除父记录时,子记录关联字段设为 NULL(需字段允许 null=True) | 作者删除时,其图书的作者字段设为 “未知” |
SET_DEFAULT | 设为默认值:删除父记录时,子记录关联字段设为默认值(需配置 default) | 部门删除时,员工的部门字段设为 “默认部门” |
DO_NOTHING | 不做任何操作:仅删除父记录,子记录关联字段保留原值(可能导致数据不一致,谨慎使用) | 特殊场景,需手动处理关联 |
3.正向查询与反向查询:
- 正向查询(子→父):通过子模型的外键字段直接访问父模型,如
book.author
(通过图书查作者); - 反向查询(父→子):通过父模型的 “子模型_set” 属性访问子模型,如
author.book_set.all()
(通过作者查所有图书)。
3.3.2 一对一关系:OneToOneField
一对一关系表示 “两个表的记录一一对应”—— 一个记录在另一个表中仅对应一个记录(如一个用户仅对应一个用户资料)。
1.适用场景:扩展已有模型的字段(如 Django 内置User
模型无法直接修改,可通过一对一关联UserProfile
扩展字段)。
2.定义语法(文档示例):
# 父模型:用户(Django内置或自定义)
class User(models.Model):username = models.CharField(max_length=100)email = models.EmailField()def __str__(self):return self.username# 子模型:用户资料(与User一对一关联)
class UserProfile(models.Model):# 一对一关联User,级联删除(用户删除时,资料也删除)user = models.OneToOneField(User, on_delete=models.CASCADE)# 扩展字段:个人简介、头像bio = models.TextField()avatar = models.ImageField(upload_to='avatars/')def __str__(self):return f"{self.user.username}的资料"
3.查询方式:
- 正向查询:
user_profile.user
(通过资料查用户); - 反向查询:
user.userprofile
(通过用户查资料,无需 “_set”,因一对一唯一)。
3.3.3 多对多关系:ManyToManyField
多对多关系表示 “两个表的记录互相多对应”—— 一个表的一条记录可对应另一个表的多条记录,反之亦然(如一篇文章可对应多个标签,一个标签可对应多篇文章)。
1.适用场景:需要 “多对多映射” 的场景,如标签、角色权限。
2.定义语法(文档示例):Django 会自动创建中间表,存储两个表的关联关系,无需手动定义中间表:
# 父模型:标签
class Tag(models.Model):name = models.CharField(max_length=50) # 标签名,如“Python”“Django”def __str__(self):return self.name# 子模型:文章(与Tag多对多关联)
class Article(models.Model):title = models.CharField(max_length=200) # 文章标题content = models.TextField() # 文章内容# 多对多关联Tag,自动创建中间表tags = models.ManyToManyField(Tag)def __str__(self):return self.title
3.查询方式:
- 正向查询(文章→标签):
article.tags.all()
(获取文章的所有标签); - 反向查询(标签→文章):
tag.article_set.all()
(获取标签对应的所有文章)。
3.4 自定义模型字段
3.4.1 自定义字段的场景
若内置字段无法匹配特殊业务场景(如存储十六进制颜色值、手机号格式校验),可通过继承 Django 内置字段,自定义字段类型。比如以 “存储十六进制颜色值” 为例,演示了自定义字段的实现。
3.4.2 自定义字段示例
需求:数据库存储 “不带 #的 6 位十六进制值”(如FF0000
),Python 代码中处理为 “带 #的标准格式”(如#FF0000
),自定义ColorField
代码如下(来自文档):
from django.db import modelsclass ColorField(models.CharField):"""自定义字段:处理十六进制颜色值"""def __init__(self, *args, **kwargs):# 强制设置max_length=7(# + 6位字符,如#FF0000)kwargs['max_length'] = 7super().__init__(*args, **kwargs)def from_db_value(self, value, expression, connection):"""从数据库读取值时:给不带#的值添加#"""if value is None:return value# 若数据库值不带#,则添加(避免重复添加)return f'#{value}' if not value.startswith('#') else valuedef to_python(self, value):"""将Python值转换为数据库存储格式:去除#"""if isinstance(value, str) and value.startswith('#'):return value[1:] # 去除#,如#FF0000→FF0000return value # 若已不带#,直接返回
3.4.3 自定义字段的核心方法
自定义字段需重写两个核心方法,实现 “数据库值” 与 “Python 值” 的转换:
from_db_value()
:数据库→Python:从数据库读取值后,转换为 Python 中方便使用的格式;to_python()
:Python→数据库:将 Python 代码中传入的值,转换为数据库可存储的格式。
3.5 Meta 类
3.5.1 Meta 类的作用
Meta 类是模型内部的一个子类,用于配置模型的 “元数据”—— 即模型本身的属性(而非数据字段),如数据库表名、默认排序、admin 界面显示名称等。
3.5.2 常用 Meta 选项
Meta 类是模型内部的一个子类,用于配置模型的 “元数据”—— 即模型本身的属性(而非数据字段),如数据库表名、默认排序、admin 界面显示名称等。
常用 Meta 选项如下,以Book
模型为例:
class Book(models.Model):title = models.CharField(max_length=200)author = models.CharField(max_length=100)publication_date = models.DateField()# Meta类:配置模型元数据class Meta:# 1. 默认排序:按出版日期降序(最新出版的在前)ordering = ['-publication_date'] # 字段前加“-”表示降序# 2. admin界面显示名称(单数/复数)verbose_name = '图书' # 单数名称(如“图书”)verbose_name_plural = '图书' # 复数名称(避免默认加“s”,如“图书s”)# 3. 自定义数据库表名(默认是“app名_模型名小写”,如myapp_book)db_table = 'book'# 4. 创建索引:为title和author创建复合索引,加速多字段查询indexes = [models.Index(fields=['title', 'author']),]
各选项的作用详解:
ordering
:默认排序字段,查询时若未指定order_by()
,则按该选项排序(如ordering=['-id']
按主键降序);verbose_name
/verbose_name_plural
:admin 界面中模型的显示名称,中文项目必须配置,否则显示英文且复数加 “s”;db_table
:自定义数据库表名,避免 Django 自动生成的 “app_模型” 格式(如将myapp_book
改为book
);indexes
:为字段创建索
3.5.3 Meta 类的使用技巧
若多个模型共享相同的 Meta 配置(如默认排序、verbose_name 格式),可通过 “模型继承” 实现 Meta 类的复用。例如,父模型定义基础 Meta 配置,子模型继承并扩展:
# 父模型:抽象基类(后续章节详解)
class BaseModel(models.Model):created_at = models.DateTimeField(auto_now_add=True)class Meta:abstract = True # 标记为抽象基类(不创建表)ordering = ['-created_at'] # 默认按创建时间降序# 子模型:继承BaseModel的Meta配置
class Book(BaseModel):title = models.CharField(max_length=200)class Meta(BaseModel.Meta): # 继承父类Metaverbose_name = '图书' # 扩展自定义名称db_table = 'book'
四、总结
本篇我们吃透了 Django 模型与 ORM 的核心基础 —— 用 Python 类抽象数据库表,靠 ORM 实现 “对象操作替代 SQL”,这才是 Django 高效开发的秘诀~
从模型定义的映射逻辑,到 MySQL 连接的配置要点,再到 “定义→迁移→渲染” 的 7 步落地闭环,每一步都是数据层的基石。尤其是字段类型的精准选择、3 种关联关系的配置技巧,还有 Meta 类的灵活用法,这些组件直接决定了数据结构的健壮性。
你是不是也踩过迁移文件冲突、关联查询混乱的坑?其实打好基础就不怕~ 下篇我们解锁 ORM 增删改查实战和性能优化,一起把 “骨架” 填成能跑的 “肌肉”!
觉得有用就点个赞吧,评论区聊聊你遇到的模型问题,一起精进!👇