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)

字段选项(Field Options)

所有字段类型共有的可选参数,用于控制字段行为。

常用选项

选项 说明 示例
null 数据库允许NULL值(默认False) null=True
blank 表单允许空值(默认False) blank=True
default 默认值 default=0default=''
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

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 字段,或使用抽象基类统一管理。