Django Model 简介
Model 是 Django 的 MTV 架构中的数据层,负责定义数据结构、数据库表结构以及数据操作逻辑。
基础示例
from django.db import models
class Article(models.Model):
# 自动创建主键 id = models.AutoField(primary_key=True)
title = models.CharField(max_length=200)
content = models.TextField()
pub_date = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
class Meta:
db_table = 'articles'
ordering = ['-pub_date']
常用数据类型映射
| Django 字段 | Python 类型 | MySQL 类型 | PostgreSQL 类型 |
|---|---|---|---|
| CharField | str | varchar | varchar |
| TextField | str | longtext | text |
| IntegerField | int | int | integer |
| DecimalField | Decimal | decimal | numeric |
| DateTimeField | datetime | datetime | timestamp |
| BooleanField | bool | tinyint(1) | boolean |
| JSONField | dict/list | json | jsonb |
💡 命名规范:
1. Model 类名使用单数形式(Article 而非 Articles)
2. 使用驼峰命名法
3. 字段名使用小写+下划线(pub_date)
1. Model 类名使用单数形式(Article 而非 Articles)
2. 使用驼峰命名法
3. 字段名使用小写+下划线(pub_date)
字段选项(Field Options)
所有字段类型共有的可选参数,用于控制字段行为。
常用选项
| 选项 | 说明 | 示例 |
|---|---|---|
| null | 数据库允许NULL值(默认False) | null=True |
| blank | 表单允许空值(默认False) | blank=True |
| default | 默认值 | default=0 或 default='' |
| choices | 选项列表(枚举) | choices=STATUS_CHOICES |
| primary_key | 设为主键(默认False) | primary_key=True |
| unique | 唯一约束(默认False) | unique=True |
| db_index | 创建数据库索引(默认False) | db_index=True |
| verbose_name | 人类可读名称 | verbose_name='标题' |
| help_text | 帮助文本(显示在表单中) | help_text='请输入标题' |
| editable | 是否可编辑(默认True) | editable=False |
null 与 blank 的区别
# null=True, blank=False(默认)- 数据库可为空,但表单验证不通过
# null=False, blank=True - 数据库不能为空,但表单可为空(存空字符串)
# null=True, blank=True - 数据库和表单都允许为空
title = models.CharField(max_length=100, blank=True) # 推荐:存空字符串而非NULL
description = models.TextField(null=True, blank=True) # 文本字段可为空
choices 选项使用
class Order(models.Model):
STATUS_CHOICES = [
('pending', '待处理'),
('processing', '处理中'),
('completed', '已完成'),
('cancelled', '已取消'),
]
status = models.CharField(
max_length=20,
choices=STATUS_CHOICES,
default='pending'
)
⚠️ 注意: CharField 和 TextField 的 null=True 与 blank=True 组合需要谨慎,通常 CharField 使用 blank=True 即可(存储空字符串),而 TextField 可以使用 null=True, blank=True。
CharField
存储短文本字符串,必须指定 max_length 参数。
定义
CharField(max_length=None, **options)
参数说明
| 参数 | 说明 | 必填 |
|---|---|---|
| max_length | 最大字符数(数据库层面限制) | 是 |
使用示例
class User(models.Model):
username = models.CharField(max_length=50, unique=True)
first_name = models.CharField(max_length=30, blank=True)
last_name = models.CharField(max_length=30, blank=True)
phone = models.CharField(max_length=20, null=True, blank=True)
💡 建议:
1. 用户名/昵称:max_length=50
2. 标题/名称:max_length=100-200
3. 电话号码:max_length=20
4. 超过255字符请使用 TextField
1. 用户名/昵称:max_length=50
2. 标题/名称:max_length=100-200
3. 电话号码:max_length=20
4. 超过255字符请使用 TextField
TextField
存储长文本内容,无长度限制(数据库层面)。
定义
TextField(**options)
使用示例
class Article(models.Model):
title = models.CharField(max_length=200)
summary = models.TextField(max_length=500, blank=True) # 摘要
content = models.TextField() # 正文内容
remark = models.TextField(null=True, blank=True) # 备注,可为空
📌 注意: TextField 在表单中渲染为 textarea 标签。虽然无长度限制,但建议通过 max_length 参数在表单层面限制,数据库层面不限制。
SlugField
存储 URL 友好的短标签,通常用于 URL 中。
定义
SlugField(max_length=50, allow_unicode=False, **options)
参数说明
| 参数 | 说明 | 默认值 |
|---|---|---|
| max_length | 最大长度 | 50 |
| allow_unicode | 允许 Unicode 字符(中文等) | False |
使用示例
class Article(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique=True)
# 中文 slug:allow_unicode=True
slug_cn = models.SlugField(allow_unicode=True)
💡 用途: 生成 SEO 友好的 URL,如:
/article/hello-world/ 而非 /article/123/
IntegerField
存储整数,有多种变体适应不同范围需求。
整数类型对比
| 字段类型 | Python 类型 | 范围 | 用途 |
|---|---|---|---|
| IntegerField | int | -2147483648 至 2147483647 | 常规整数 |
| BigIntegerField | int | -9223372036854775808 至 9223372036854775807 | 大整数(如用户ID) |
| SmallIntegerField | int | -32768 至 32767 | 小范围整数(状态码) |
| PositiveIntegerField | int | 0 至 2147483647 | 非负整数 |
| PositiveSmallIntegerField | int | 0 至 32767 | 非负小整数 |
使用示例
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.PositiveIntegerField() # 价格(分),非负
stock = models.IntegerField(default=0) # 库存,可为负(超卖)
sort_order = models.SmallIntegerField(default=0) # 排序,小范围
sales_count = models.BigIntegerField(default=0) # 销量统计,可能很大
⚠️ 注意: 不要用 IntegerField 存储货币!请使用 DecimalField 避免浮点精度问题。
DecimalField
存储固定精度的十进制数,适合货币、财务计算。
定义
DecimalField(max_digits=None, decimal_places=None, **options)
参数说明
| 参数 | 说明 | 示例 |
|---|---|---|
| max_digits | 总位数(包括小数位) | 5 表示 -999.99 至 999.99 |
| decimal_places | 小数位数 | 2 表示保留两位小数 |
使用示例
class Order(models.Model):
# 价格:最大9999.99,两位小数
price = models.DecimalField(max_digits=6, decimal_places=2)
# 金额:最大99999999.99
amount = models.DecimalField(max_digits=10, decimal_places=2, default=0)
# 高精度:8位小数
rate = models.DecimalField(max_digits=15, decimal_places=8)
🚫 重要: 两个参数必须同时设置!Python 中使用
Decimal 类型,不要与 float 混用。
FloatField
存储浮点数,使用 Python 的 float 类型。
定义
FloatField(**options)
使用示例
class Location(models.Model):
name = models.CharField(max_length=100)
latitude = models.FloatField() # 纬度
longitude = models.FloatField() # 经度
temperature = models.FloatField(null=True, blank=True) # 温度
⚠️ 警告: 浮点数存在精度问题(0.1 + 0.2 ≠ 0.3),永远不要用于货币计算!请使用 DecimalField。
其他数值字段
AutoField
自增整数主键,自动创建,通常无需显式定义。
id = models.AutoField(primary_key=True) # Django 自动添加
BigAutoField
64位自增整数,用于数据量极大的表。
class Log(models.Model):
id = models.BigAutoField(primary_key=True)
message = models.TextField()
BinaryField
存储二进制数据(图片、文件等),但通常使用 FileField 更好。
data = models.BinaryField() # 存储 bytes 类型
DateTimeField
存储日期和时间,Python 中使用 datetime 对象。
定义
DateTimeField(auto_now=False, auto_now_add=False, **options)
自动填充参数
| 参数 | 说明 | 使用场景 |
|---|---|---|
| auto_now | 每次保存时自动设为当前时间 | 最后修改时间 |
| auto_now_add | 创建时自动设为当前时间,之后不变 | 创建时间 |
使用示例
class Article(models.Model):
title = models.CharField(max_length=200)
created_at = models.DateTimeField(auto_now_add=True) # 自动记录创建时间
updated_at = models.DateTimeField(auto_now=True) # 自动记录更新时间
publish_time = models.DateTimeField(null=True, blank=True) # 手动设置发布时间
💡 注意: auto_now 和 auto_now_add 互斥,不能同时使用。两者都为 False 时需要手动赋值。
DateField
存储日期(年-月-日),Python 中使用 date 对象。
定义
DateField(auto_now=False, auto_now_add=False, **options)
使用示例
class Person(models.Model):
name = models.CharField(max_length=50)
birth_date = models.DateField() # 出生日期
join_date = models.DateField(auto_now_add=True) # 入职日期
TimeField
存储时间(时:分:秒),Python 中使用 time 对象。
使用示例
class Event(models.Model):
name = models.CharField(max_length=100)
start_time = models.TimeField() # 开始时间 09:00:00
duration = models.DurationField() # 持续时间
DurationField
存储时间跨度,Python 中使用 timedelta 对象。
使用示例
from datetime import timedelta
class Task(models.Model):
name = models.CharField(max_length=100)
estimated_time = models.DurationField(default=timedelta(hours=1))
actual_time = models.DurationField(null=True, blank=True)
📌 存储格式: 数据库存储为 bigint 微秒数,如 1小时 = 3600000000
BooleanField
存储 True/False 值。
定义
BooleanField(**options)
使用示例
class Task(models.Model):
title = models.CharField(max_length=200)
is_completed = models.BooleanField(default=False)
is_urgent = models.BooleanField(default=False)
is_deleted = models.BooleanField(default=False, db_index=True)
NullBooleanField(已弃用)
Django 3.1+ 使用 BooleanField(null=True) 代替
# 旧版本
is_active = models.NullBooleanField()
# 新版本(推荐)
is_active = models.BooleanField(null=True, blank=True)
EmailField
存储邮箱地址,自动验证格式。
定义
EmailField(max_length=254, **options)
使用示例
class User(models.Model):
username = models.CharField(max_length=50)
email = models.EmailField(unique=True)
backup_email = models.EmailField(null=True, blank=True)
💡 验证规则: 自动检查 @ 符号和域名格式,但不验证邮箱是否真实存在。
URLField
存储 URL 地址,自动验证格式。
定义
URLField(max_length=200, **options)
使用示例
class Website(models.Model):
name = models.CharField(max_length=100)
url = models.URLField()
logo = models.URLField(null=True, blank=True)
github = models.URLField(
max_length=100,
null=True,
blank=True,
verbose_name='GitHub地址'
)
UUIDField
存储 UUID(通用唯一识别码),适合分布式系统主键。
定义
UUIDField(**options)
使用示例
import uuid
from django.db import models
class APIToken(models.Model):
id = models.UUIDField(
primary_key=True,
default=uuid.uuid4,
editable=False
)
name = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
📌 优点: 全局唯一,无需中央协调生成,适合微服务架构。
FileField 与 ImageField
存储上传的文件,ImageField 额外验证图片格式。
定义
FileField(upload_to='', storage=None, max_length=100, **options)
ImageField(upload_to='', height_field=None, width_field=None, **options)
参数说明
| 参数 | 说明 |
|---|---|
| upload_to | 上传路径(字符串或函数) |
| storage | 自定义存储后端(如OSS、S3) |
| height_field | 自动填充图片高度的字段名 |
| width_field | 自动填充图片宽度的字段名 |
使用示例
def user_directory_path(instance, filename):
# 文件将上传到 MEDIA_ROOT/user_/
return f'user_{instance.user.id}/{filename}'
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
avatar = models.ImageField(
upload_to='avatars/',
height_field='avatar_height',
width_field='avatar_width',
blank=True
)
avatar_height = models.PositiveIntegerField(null=True, blank=True)
avatar_width = models.PositiveIntegerField(null=True, blank=True)
resume = models.FileField(upload_to=user_directory_path, blank=True)
⚠️ 配置要求: 必须在 settings.py 中配置 MEDIA_ROOT 和 MEDIA_URL,并设置路由提供文件访问。
settings.py 配置
MEDIA_ROOT = BASE_DIR / 'media' # 文件存储路径
MEDIA_URL = '/media/' # URL访问路径
JSONField
存储 JSON 数据,Python 中使用 dict/list。
定义
JSONField(encoder=None, **options)
使用示例
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
settings = models.JSONField(default=dict, blank=True) # 用户设置
tags = models.JSONField(default=list, blank=True) # 标签列表
metadata = models.JSONField(null=True, blank=True) # 元数据
查询语法
# 查询 JSON 字段
UserProfile.objects.filter(settings__theme='dark')
UserProfile.objects.filter(tags__contains='python')
UserProfile.objects.filter(metadata__isnull=False)
📌 数据库支持: MySQL 5.7+、PostgreSQL、SQLite 3.9+ 原生支持 JSON 类型,旧版本使用文本存储。
ForeignKey(多对一)
建立多对一关系,如多篇文章属于一个作者。
定义
ForeignKey(to, on_delete, **options)
必需参数
| 参数 | 说明 |
|---|---|
| to | 关联的 Model 类(字符串或类) |
| on_delete | 关联对象删除时的行为 |
on_delete 选项
| 选项 | 说明 |
|---|---|
| CASCADE | 级联删除(默认) |
| PROTECT | 阻止删除(抛出 ProtectedError) |
| SET_NULL | 设为 NULL(字段需 null=True) |
| SET_DEFAULT | 设为默认值 |
| DO_NOTHING | 不操作(可能引发数据库错误) |
使用示例
class Category(models.Model):
name = models.CharField(max_length=50)
class Article(models.Model):
title = models.CharField(max_length=200)
category = models.ForeignKey(
Category,
on_delete=models.SET_NULL,
null=True,
related_name='articles' # 反向查询名
)
author = models.ForeignKey(
'auth.User',
on_delete=models.CASCADE
)
查询方式
# 正向查询(文章 → 分类)
article.category
article.category_id # 直接获取ID,不查询数据库
# 反向查询(分类 → 文章)
category.articles.all()
category.articles.filter(status='published')
⚠️ 注意: 外键字段在数据库中会自动添加
_id 后缀(如 category_id),但 Python 中使用不带后缀的名称。
ManyToManyField(多对多)
建立多对多关系,如文章可以有多个标签,标签可以属于多篇文章。
定义
ManyToManyField(to, **options)
常用参数
| 参数 | 说明 |
|---|---|
| through | 指定中间模型(用于添加额外字段) |
| symmetrical | 是否对称(仅自关联时使用) |
基础使用
class Tag(models.Model):
name = models.CharField(max_length=50, unique=True)
class Article(models.Model):
title = models.CharField(max_length=200)
tags = models.ManyToManyField(Tag, related_name='articles', blank=True)
带中间模型的多对多
class Person(models.Model):
name = models.CharField(max_length=50)
class Group(models.Model):
name = models.CharField(max_length=50)
members = models.ManyToManyField(
Person,
through='Membership',
through_fields=['group', 'person']
)
class Membership(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
操作方式
# 添加关系
article.tags.add(tag1, tag2)
article.tags.set([tag1, tag2]) # 替换所有
# 移除关系
article.tags.remove(tag1)
article.tags.clear() # 移除所有
# 查询
Article.objects.filter(tags__name='python')
tag.articles.all()
📌 注意: 多对多关系会自动创建中间表,不需要手动管理。
OneToOneField(一对一)
建立一对一关系,如用户和用户资料。
定义
OneToOneField(to, on_delete, **options)
使用示例
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(
User,
on_delete=models.CASCADE,
related_name='profile'
)
bio = models.TextField(blank=True)
birth_date = models.DateField(null=True, blank=True)
location = models.CharField(max_length=100, blank=True)
查询方式
# 正向查询
profile = user.profile
# 反向查询(与正向相同,因为是一对一)
user = profile.user
# 查询
User.objects.filter(profile__bio__icontains='python')
strong>💡 场景: 扩展 User 模型、国家-首都、产品-详细规格等一对一关系。
Meta 配置选项
通过内部 Meta 类配置 Model 的元数据。
常用选项
| 选项 | 说明 | 示例 |
|---|---|---|
| db_table | 数据库表名 | 'my_table' |
| ordering | 默认排序字段 | ['-created_at'] |
| verbose_name | 单数可读名称 | '文章' |
| verbose_name_plural | 复数可读名称 | '文章列表' |
| unique_together | 联合唯一约束 | [('field1', 'field2')] |
| indexes | 自定义索引 | [models.Index(fields=['name'])] |
| abstract | 是否为抽象基类 | True |
| managed | 是否由 Django 管理 | False(现有表) |
完整示例
class Article(models.Model):
title = models.CharField(max_length=200, verbose_name='标题')
content = models.TextField(verbose_name='内容')
created_at = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
db_table = 'blog_articles'
ordering = ['-created_at']
verbose_name = '文章'
verbose_name_plural = '文章管理'
unique_together = [['title', 'author']] # 同一作者标题唯一
indexes = [
models.Index(fields=['created_at'], name='created_idx'),
]
抽象基类
class BaseModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True # 抽象基类,不会创建表
class Article(BaseModel): # 继承所有字段
title = models.CharField(max_length=200)
💡 建议: 为所有模型添加 created_at 和 updated_at 字段,或使用抽象基类统一管理。