在Django模型开发中,单字段的唯一性约束可以通过给字段添加unique=True参数快速实现,但面对多个字段组合需要保持唯一的业务场景时,就需要使用复合字段唯一性约束。这种约束能确保多个字段的组合值在数据库表中不会重复,比如同一个用户在同一天只能创建一条记录,就需要把用户字段和日期字段作为复合约束。

单字段唯一性约束回顾
先回顾一下单字段唯一约束的实现方式,只需要在模型字段定义时添加unique=True参数即可,Django会自动在数据库层面为该字段创建唯一索引,插入重复值时会抛出IntegrityError异常。
示例代码如下:
from django.db import models
class User(models.Model):
# 用户名设置唯一约束,不允许重复
username = models.CharField(max_length=50, unique=True)
create_time = models.DateTimeField(auto_now_add=True)
复合字段唯一性约束实现方式
方式一:使用Meta类的unique_together属性
这是Django早期就支持的复合唯一约束配置方式,在模型的Meta内部类中定义unique_together参数,参数值为一个元组或列表,每个元素是需要组合唯一的字段名称元组。
比如要实现同一分类下商品名称不能重复的需求,就可以把分类字段和商品名称字段组合为唯一约束:
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=50)
class Product(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
class Meta:
# 复合唯一约束:同一分类下商品名称不能重复
unique_together = (('category', 'name'),)
# 也可以写成列表形式
# unique_together = ['category', 'name']
配置完成后执行makemigrations和migrate命令,Django会在数据库层面为这两个字段的组合创建唯一索引,当尝试插入同一分类下相同名称的商品时,会触发数据库的唯一约束异常。
方式二:使用UniqueConstraint(推荐)
Django 2.2版本之后推荐使用UniqueConstraint来定义唯一约束,它比unique_together支持更多功能,比如可以设置条件约束、自定义约束名称等,配置方式是在Meta类的constraints属性中添加UniqueConstraint实例。
同样实现上述商品分类和名称的复合唯一约束,使用UniqueConstraint的代码如下:
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=50)
class Product(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
is_deleted = models.BooleanField(default=False)
class Meta:
constraints = [
# 复合唯一约束,还可以自定义约束名称
models.UniqueConstraint(
fields=['category', 'name'],
name='unique_product_name_per_category'
),
# 还可以添加条件约束,比如只约束未删除的商品
# models.UniqueConstraint(
# fields=['category', 'name'],
# name='unique_active_product_name_per_category',
# condition=models.Q(is_deleted=False)
# )
]
两种方式的对比
两种方式的核心作用都是实现复合字段唯一性约束,差异如下:
| 对比项 | unique_together | UniqueConstraint |
|---|---|---|
| 支持版本 | Django全版本支持 | Django 2.2及以上版本支持 |
| 条件约束 | 不支持 | 支持通过condition参数设置条件 |
| 自定义名称 | 不支持,自动生成名称 | 支持自定义约束名称 |
| 官方推荐度 | 不推荐新使用 | 推荐使用 |
注意事项
- 复合唯一约束中的字段可以是普通字段,也可以是外键字段,外键字段需要写关联字段的名称,不是模型类名。
- 约束会在数据库迁移时同步到数据库层面,修改约束后需要重新执行迁移命令才能生效。
- 当插入数据触发唯一约束时,Django会抛出
django.db.utils.IntegrityError异常,可以在业务逻辑中捕获该异常进行处理,返回友好的提示信息。 - 如果模型已经有数据,添加复合唯一约束时需要确保现有数据不存在重复的组合值,否则迁移会失败。
约束生效验证
可以通过Django的ORM操作验证约束是否生效,示例代码如下:
from django.db import transaction
from django.db.utils import IntegrityError
from .models import Category, Product
# 创建测试分类
category = Category.objects.create(name='电子产品')
# 第一次创建商品,正常插入
Product.objects.create(category=category, name='手机', price=1999.00)
# 第二次创建同一分类下的同名商品,会触发唯一约束异常
try:
with transaction.atomic():
Product.objects.create(category=category, name='手机', price=2999.00)
except IntegrityError as e:
print('插入失败,该分类下已存在同名商品')
通过上述代码可以看到,当尝试插入重复的组合数据时,会成功捕获到异常,说明复合字段唯一性约束已经生效。
Django模型复合字段唯一性约束Meta_class修改时间:2026-07-01 10:39:44