Django 模型字段是连接 Python 类与数据库列的核心载体,字段的定义决定了数据库表的结构(列类型、约束、默认值等),同时也影响表单验证、ORM 操作等上层逻辑。以下是 Django 模型字段的完整定义规则、核心类型、字段选项及最佳实践,帮助你精准定义符合业务需求的字段。

一、字段定义的基本语法

模型字段的定义遵循「字段类型 + 字段选项」的结构,核心语法:

from django.db import models

class 模型类名(models.Model):
    字段名 = models.字段类型(字段选项1=值1, 字段选项2=值2, ...)

核心规则:

  1. 字段名:小写字母 + 下划线(如 create_time),避免 Python 关键字(class/def)和 SQL 关键字(select/where)。
  2. 字段类型:Django 内置的字段类(如 CharField/IntegerField),对应数据库的列类型(如 VARCHAR/INT)。
  3. 字段选项:可选参数,用于约束字段行为(如非空、默认值、索引等)。

二、核心字段类型(按用途分类)

Django 提供了丰富的内置字段类型,覆盖绝大多数数据库列类型,以下是高频使用的类型及场景:

1. 字符串/文本类

字段类型数据库类型适用场景必选参数
CharFieldVARCHAR短文本(标题、用户名、手机号)max_length
TextFieldTEXT长文本(文章内容、备注)
SlugFieldVARCHARURL 友好的短文本(仅字母/数字/下划线)max_length
EmailFieldVARCHAR邮箱地址(自带格式验证)max_length(默认75)

示例

title = models.CharField(max_length=200, verbose_name="文章标题")  # 短文本
content = models.TextField(verbose_name="文章内容")                # 长文本
user_email = models.EmailField(verbose_name="用户邮箱")            # 邮箱验证

2. 数值类

字段类型数据库类型适用场景关键参数
IntegerFieldINT整数(年龄、数量、ID)
BigIntegerFieldBIGINT大整数(超过 INT 范围,如雪花ID)
FloatFieldFLOAT浮点数(精度要求低的小数)
DecimalFieldDECIMAL高精度小数(金额、汇率)max_digits(总位数)、decimal_places(小数位)
PositiveIntegerFieldINT正整数(非负,如库存)

示例

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. 日期/时间类

字段类型数据库类型适用场景关键参数
DateFieldDATE日期(年月日,如生日)auto_now/auto_now_add/default
DateTimeFieldDATETIME日期时间(年月日时分秒,如创建时间)同上
TimeFieldTIME时间(时分秒,如打卡时间)同上
DurationFieldINTERVAL时间间隔(如任务耗时)

核心参数说明

  • 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. 布尔/选择类

字段类型数据库类型适用场景关键参数
BooleanFieldBOOLEAN布尔值(是否发布、是否启用)default(建议设置)
NullBooleanFieldBOOLEAN支持 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一对一用户-个人资料、订单-发票toon_delete
ManyToManyField多对多文章-标签、学生-课程torelated_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_toheight_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. 其他常用类型

字段类型数据库类型适用场景
UUIDFieldCHAR(32)唯一标识符(替代自增ID,如订单号)
IPAddressFieldCHAR(15)IPv4 地址(如用户登录IP)
GenericIPAddressFieldCHAR(39)IPv4/IPv6 地址
JSONFieldJSON存储 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.nowdefault=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=Trueblank=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="手机号")

四、字段定义的最佳实践

  1. 主键设计

    • Django 自动为模型生成 id 自增主键(IntegerField + primary_key=True);
    • 若需自定义主键(如 UUID),显式设置 primary_key=True,且一个模型只能有一个主键。
  2. 避免冗余字段

    • 不要存储可通过计算得到的值(如「总价」= 单价 × 数量),可通过模型方法实现:

      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
  3. 索引优化

    • 为高频查询字段(如 user_idcreate_time)设置 db_index=True
    • 多字段联合查询用 indexes(Meta 中配置):

      class Meta:
          indexes = [
              models.Index(fields=["user_id", "-create_time"]),  # 联合索引,按 create_time 降序
          ]
  4. 数据类型匹配

    • 金额用 DecimalField 而非 FloatField(避免浮点精度丢失);
    • 长文本用 TextField,短文本用 CharField(数据库存储效率更高)。
  5. 兼容性考虑

    • 新增字段时,若模型已有数据,需设置 null=Truedefault,避免迁移报错;
    • 生产环境修改字段类型(如 CharField → IntegerField)前,先验证数据兼容性。

五、常见错误与避坑

  1. 忘记设置 max_lengthCharField 必须指定 max_length,否则迁移报错;
  2. null=True 滥用:字符串字段设置 null=True 会导致数据库同时存在空字符串和 NULL,建议仅设置 blank=True
  3. auto_nowdefault 冲突auto_now=True 会覆盖 default,二者不能同时使用;
  4. 关联字段漏写 on_deleteForeignKey/OneToOneField 必须指定 on_delete 参数,否则报错。

通过以上规则和示例,你可以根据业务场景精准定义 Django 模型字段,兼顾数据库结构合理性、查询性能和开发便捷性。
(来自豆包)

标签: none