首页 > 解决方案 > Django 创建唯一函数索引

问题描述

使用 postgresql 我们可以创建唯一的函数索引:

create unique index user_date_checkin
on unique_user_date (user_id, (timezone('UTC'::text, create_time)::date));

但是对于 Django 3.2:

class UserCheckin(GeneralModel):
   id = models.BigAutoField(primary_key=True)
   user_id = models.BigIntegerField()
   create_time = models.DateTimeField()
   
   class Meta:
       indexes = [
           models.Index("user_id", TruncDate("create_time"), name="user_date_checkin"),
        ]

只能得到这样的sql生成:

create index user_date_checkin
    on test (user_id, (timezone('UTC'::text, create_time)::date));

UniqueConstraint

constraints = [
    models.UniqueConstraint(
        fields=["user_id", TruncDate("create_time")],
        name="user_date"),
]

refers to the nonexistent field 'TruncDate(F(create_time))出错了

那么如何在 Django 3.2 中创建具有函数的唯一索引?

更新

在 django 源代码django/db/backends/base/schema.py中,我发现:

    if condition or include or opclasses:
        sql = self.sql_create_unique_index
    else:
        sql = self.sql_create_unique

但在这种情况下我没有这样的条件或操作类

更新

添加条件后:

    indexes = [
        models.Index("user_id", TruncDate("create_time"),
            condition=Q(user_id__isnull=False), name="user_date_checkin"),
    ]

还是不要在unique这里添加:

-- auto-generated definition
create index user_date_checkin
    on voip_usercheckin (user_id, (timezone('UTC'::text, create_time)::date))
    where (user_id IS NOT NULL);

标签: djangopostgresqldjango-models

解决方案


Django 还不支持功能约束。但此功能已经在开发中,将成为 Django 4.0 版的一部分,预计将于 2021 年 12 月发布。现在您可以通过自定义迁移执行您的 sql:

首先运行您拥有模型的应用程序在哪里python manage.py makemigrations yourapp --emptyyourapp接下来编辑生成的迁移并添加RunSQL[Django-docs]操作:

from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ...
    ]

    operations = [
        migrations.RunSQL(
            sql="create unique index user_date_checkin on unique_user_date (user_id, (timezone('UTC'::text, create_time)::date));",
            reverse_sql="DROP INDEX IF EXISTS user_date_checkin;"
        ),
    ]

python manage.py migrate在此之后,您可以通过运行将在数据库中创建索引来应用此迁移。


在 Django 4.0 发布后,您可能(可能会更改)编写以下内容:

constraints = [
    models.UniqueConstraint(
        "user_id",
        TruncDate("create_time"),
        name="user_date"),
]

推荐阅读