You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django3.2+PostgreSQL12中collation与unique_together不生效问题

解决Django 3.2 + PostgreSQL 12下组合唯一约束的大小写不敏感问题

问题根源

你遇到的问题是:虽然给identifier字段指定了大小写不敏感的collation,但Django的unique_together生成的组合唯一约束,并没有自动使用该字段的collation,而是采用了数据库默认的大小写敏感规则,导致ab和Ab被判定为不同值,无法触发唯一约束报错。

解决方案

需要手动创建带有指定collation的组合唯一约束,替代Django自动生成的约束。具体步骤如下:

  1. 修改模型配置
    移除模型中Meta类的unique_together配置:
class MyItem(models.Model):
    identifier = models.CharField(db_collation="case_insensitive", db_index=True, max_length=100, null=True)
    container = models.ForeignKey(Container, related_name='my_items', on_delete=models.CASCADE, null=True)
    
    class Meta:
        # 移除原有的unique_together
        pass
  1. 生成空迁移文件
    在项目根目录执行命令,生成一个空的迁移文件:
python manage.py makemigrations --empty your_app_name
  1. 编辑迁移文件添加自定义约束
    打开生成的迁移文件,替换内容为以下代码,通过原生SQL创建带有指定collation的组合唯一约束:
from django.db import migrations

class Migration(migrations.Migration):
    dependencies = [
        ('your_app_name', '之前的迁移文件名'),  # 替换为实际的依赖迁移名
    ]

    operations = [
        migrations.RunSQL(
            sql="""
            ALTER TABLE your_app_name_myitem
            ADD CONSTRAINT unique_myitem_identifier_container
            UNIQUE (identifier COLLATE "case_insensitive", container_id);
            """,
            reverse_sql="""
            ALTER TABLE your_app_name_myitem
            DROP CONSTRAINT IF EXISTS unique_myitem_identifier_container;
            """,
        ),
    ]

注意替换代码中的your_app_name为你的应用名称,以及之前的迁移文件名为实际的依赖迁移名称。

  1. 执行迁移
    执行迁移使约束生效:
python manage.py migrate

验证效果

迁移完成后,尝试在同一个container下创建identifier为ab和Ab的两个MyItem对象,此时数据库会抛出唯一约束冲突的报错,符合预期需求。

额外检查

确保之前创建的case_insensitive collation已成功生效,可通过PostgreSQL命令行执行以下语句验证:

SELECT * FROM pg_collation WHERE collname = 'case_insensitive';

若能查询到对应的记录,则表示collation创建成功。

内容的提问来源于stack exchange,提问作者user2880391

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 00:03:30