django模型字段的定义
Django 模型字段是连接 Python 类与数据库列的核心载体,字段的定义决定了数据库表的结构(列类型、约束、默认值等),同时也影响表单验证、ORM 操作等上层逻辑。以下是 Django 模型字段的完整定义规则、核心类型、字段选项及最佳实践,帮助你精准定义符合业务需求的字段。
一、字段定义的基本语法
模型字段的定义遵循「字段类型 + 字段选项」的结构,核心语法:
from django.db import models
class 模型类名(models.Model):
字段名 = models.字段类型(字段选项1=值1, 字段选项2=值2, ...)核心规则:
- 字段名:小写字母 + 下划线(如
create_time),避免 Python 关键字(class/def)和 SQL 关键字(select/where)。 - 字段类型:Django 内置的字段类(如
CharField/IntegerField),对应数据库的列类型(如VARCHAR/INT)。 - 字段选项:可选参数,用于约束字段行为(如非空、默认值、索引等)。
二、核心字段类型(按用途分类)
Django 提供了丰富的内置字段类型,覆盖绝大多数数据库列类型,以下是高频使用的类型及场景:
1. 字符串/文本类
| 字段类型 | 数据库类型 | 适用场景 | 必选参数 |
|---|---|---|---|
CharField | VARCHAR | 短文本(标题、用户名、手机号) | max_length |
TextField | TEXT | 长文本(文章内容、备注) | 无 |
SlugField | VARCHAR | URL 友好的短文本(仅字母/数字/下划线) | max_length |
EmailField | VARCHAR | 邮箱地址(自带格式验证) | max_length(默认75) |
示例:
title = models.CharField(max_length=200, verbose_name="文章标题") # 短文本
content = models.TextField(verbose_name="文章内容") # 长文本
user_email = models.EmailField(verbose_name="用户邮箱") # 邮箱验证2. 数值类
| 字段类型 | 数据库类型 | 适用场景 | 关键参数 |
|---|---|---|---|
IntegerField | INT | 整数(年龄、数量、ID) | 无 |
BigIntegerField | BIGINT | 大整数(超过 INT 范围,如雪花ID) | 无 |
FloatField | FLOAT | 浮点数(精度要求低的小数) | 无 |
DecimalField | DECIMAL | 高精度小数(金额、汇率) | max_digits(总位数)、decimal_places(小数位) |
PositiveIntegerField | INT | 正整数(非负,如库存) | 无 |
示例:
age = models.PositiveIntegerField(verbose_name="年龄")
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="商品价格") # 如 99999999.99
score = models.FloatField(default=0.0, verbose_name="评分")3. 日期/时间类
| 字段类型 | 数据库类型 | 适用场景 | 关键参数 |
|---|---|---|---|
DateField | DATE | 日期(年月日,如生日) | auto_now/auto_now_add/default |
DateTimeField | DATETIME | 日期时间(年月日时分秒,如创建时间) | 同上 |
TimeField | TIME | 时间(时分秒,如打卡时间) | 同上 |
DurationField | INTERVAL | 时间间隔(如任务耗时) | 无 |
核心参数说明:
auto_now=True:每次保存实例时自动更新为当前时间(如更新时间);auto_now_add=True:实例创建时自动设置为当前时间(如创建时间);default:手动设置默认值(如default=timezone.now)。
示例:
from django.utils import timezone
create_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") # 仅创建时赋值
update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间") # 每次保存更新
birthday = models.DateField(null=True, blank=True, verbose_name="生日") # 可选日期4. 布尔/选择类
| 字段类型 | 数据库类型 | 适用场景 | 关键参数 |
|---|---|---|---|
BooleanField | BOOLEAN | 布尔值(是否发布、是否启用) | default(建议设置) |
NullBooleanField | BOOLEAN | 支持 NULL 的布尔值(是/否/未知) | 无 |
ChoiceField | —(模型中用 choices 替代) | 固定选项(如性别、状态) | 模型字段通过 choices 参数实现 |
示例:
# 布尔字段
is_published = models.BooleanField(default=False, verbose_name="是否发布")
is_valid = models.NullBooleanField(verbose_name="是否有效") # 可空布尔
# 选择字段(核心是 choices 参数)
GENDER_CHOICES = (
('M', '男'),
('F', '女'),
('U', '未知'),
)
gender = models.CharField(max_length=1, choices=GENDER_CHOICES, default='U', verbose_name="性别")
# 数值型选择字段
STATUS_CHOICES = (
(0, '草稿'),
(1, '发布'),
(2, '下架'),
)
status = models.IntegerField(choices=STATUS_CHOICES, default=0, verbose_name="文章状态")5. 关联类(表关系)
| 字段类型 | 关系类型 | 适用场景 | 关键参数 |
|---|---|---|---|
ForeignKey | 一对多 | 文章-作者、订单-用户 | to(关联模型)、on_delete(级联规则) |
OneToOneField | 一对一 | 用户-个人资料、订单-发票 | to、on_delete |
ManyToManyField | 多对多 | 文章-标签、学生-课程 | to、related_name(反向关联名) |
示例:
# 一对多:文章属于某个分类
class Category(models.Model):
name = models.CharField(max_length=50, verbose_name="分类名称")
class Article(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE, verbose_name="所属分类")
# on_delete 可选值:
# CASCADE:删除分类时,关联文章也删除;
# SET_NULL:删除分类时,文章的 category 设为 NULL(需设置 null=True);
# PROTECT:禁止删除有关联文章的分类;
# SET_DEFAULT:删除分类时,设为默认值(需设置 default)。
# 多对多:文章有多个标签
class Tag(models.Model):
name = models.CharField(max_length=20, verbose_name="标签名称")
class Article(models.Model):
tags = models.ManyToManyField(Tag, related_name="articles", verbose_name="标签")6. 文件/图片类
| 字段类型 | 存储方式 | 适用场景 | 关键参数 |
|---|---|---|---|
FileField | 存储文件路径 | 上传任意文件(文档、压缩包) | upload_to(上传路径) |
ImageField | 存储图片路径 | 上传图片(自带尺寸/格式验证) | upload_to、height_field/width_field(记录尺寸) |
示例:
# 需先配置 MEDIA_ROOT 和 MEDIA_URL(settings.py)
import os
def user_avatar_path(instance, filename):
# 自定义上传路径:media/avatar/用户ID/文件名
return os.path.join('avatar', str(instance.user.id), filename)
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
avatar = models.ImageField(upload_to=user_avatar_path, null=True, blank=True, verbose_name="用户头像")
attachment = models.FileField(upload_to="files/%Y/%m/%d", verbose_name="附件") # 按日期分目录7. 其他常用类型
| 字段类型 | 数据库类型 | 适用场景 |
|---|---|---|
UUIDField | CHAR(32) | 唯一标识符(替代自增ID,如订单号) |
IPAddressField | CHAR(15) | IPv4 地址(如用户登录IP) |
GenericIPAddressField | CHAR(39) | IPv4/IPv6 地址 |
JSONField | JSON | 存储 JSON 数据(如配置、嵌套结构) |
示例:
import uuid
order_id = models.UUIDField(default=uuid.uuid4, editable=False, verbose_name="订单ID")
user_ip = models.GenericIPAddressField(null=True, blank=True, verbose_name="登录IP")
config = models.JSONField(default=dict, verbose_name="配置信息") # 默认空字典三、通用字段选项(所有字段均可使用)
字段选项是对字段的约束和扩展,以下是高频使用的选项:
| 选项名 | 作用 | 示例 |
|---|---|---|
verbose_name | 字段的人性化名称(后台/admin 显示) | verbose_name="文章标题" |
null | 数据库层面:是否允许列为 NULL(默认 False) | null=True |
blank | 表单层面:是否允许为空(默认 False)(null 是数据库约束,blank 是表单验证) | blank=True |
default | 字段默认值(可以是固定值、可调用对象如 timezone.now) | default=0/default=timezone.now |
unique | 数据库层面:字段值是否唯一(默认 False) | unique=True |
db_index | 是否为字段创建数据库索引(加速查询) | db_index=True |
primary_key | 是否设为主键(默认 False,Django 自动为模型生成 id 主键) | primary_key=True |
editable | 是否在 admin/表单中显示并允许编辑(默认 True) | editable=False |
help_text | 表单/后台中显示的帮助文本 | help_text="请输入不超过200字的标题" |
db_column | 自定义数据库列名(默认是字段名) | db_column="article_title" |
validators | 自定义验证器(列表形式,用于表单/模型验证) | validators=[MinValueValidator(0)] |
关键区分:null vs blank
| 场景 | null=True | blank=True |
|---|---|---|
| 字符串字段(CharField) | 不推荐(会存空字符串 + NULL) | 推荐(存空字符串) |
| 数值/日期字段 | 推荐(允许 NULL) | 配合 null=True 使用 |
| 表单允许为空 | 无需设置 | 必须设置 |
示例:
# 字符串字段:允许表单为空,数据库存空字符串(不设 null=True)
nickname = models.CharField(max_length=50, blank=True, verbose_name="昵称")
# 日期字段:允许为空,数据库存 NULL
birthday = models.DateField(null=True, blank=True, verbose_name="生日")
# 唯一且带索引的手机号
phone = models.CharField(max_length=11, unique=True, db_index=True, verbose_name="手机号")四、字段定义的最佳实践
主键设计:
- Django 自动为模型生成
id自增主键(IntegerField + primary_key=True); - 若需自定义主键(如 UUID),显式设置
primary_key=True,且一个模型只能有一个主键。
- Django 自动为模型生成
避免冗余字段:
不要存储可通过计算得到的值(如「总价」= 单价 × 数量),可通过模型方法实现:
class Order(models.Model): price = models.DecimalField(max_digits=10, decimal_places=2) num = models.IntegerField(default=1) @property def total_price(self): return self.price * self.num
索引优化:
- 为高频查询字段(如
user_id、create_time)设置db_index=True; 多字段联合查询用
indexes(Meta 中配置):class Meta: indexes = [ models.Index(fields=["user_id", "-create_time"]), # 联合索引,按 create_time 降序 ]
- 为高频查询字段(如
数据类型匹配:
- 金额用
DecimalField而非FloatField(避免浮点精度丢失); - 长文本用
TextField,短文本用CharField(数据库存储效率更高)。
- 金额用
兼容性考虑:
- 新增字段时,若模型已有数据,需设置
null=True或default,避免迁移报错; - 生产环境修改字段类型(如 CharField → IntegerField)前,先验证数据兼容性。
- 新增字段时,若模型已有数据,需设置
五、常见错误与避坑
- 忘记设置
max_length:CharField必须指定max_length,否则迁移报错; null=True滥用:字符串字段设置null=True会导致数据库同时存在空字符串和 NULL,建议仅设置blank=True;auto_now与default冲突:auto_now=True会覆盖default,二者不能同时使用;- 关联字段漏写
on_delete:ForeignKey/OneToOneField必须指定on_delete参数,否则报错。
通过以上规则和示例,你可以根据业务场景精准定义 Django 模型字段,兼顾数据库结构合理性、查询性能和开发便捷性。
(来自豆包)