UniqueConstraint 并忽略区分大小写

问题描述 投票:0回答:4

我想使用此代码:

constraints = [
    models.UniqueConstraint(fields=['name', 'app'], name='unique_booking'),
]

但名称和应用程序(两者)不应检查区分大小写,因此“FOo”和“fOO”应标记为同一应用程序。怎么办?

django django-models
4个回答
2
投票

有 2 个选项:

第一个选项:

如果您使用的 postgresql 和 Django 版本高于 2.0 - 您可以使用 CICharField 或 CITextField 字段。

你的模型看起来像这样:

class DemoModelCI(models.Model):

    class Meta(object):
        constraints = [
            UniqueConstraint(fields=['app', 'name'], name='unique_booking_ci')
        ]

    name = CICharField(null=False, default=None, max_length=550, blank=False)
    app = CICharField(null=False, default=None, max_length=550, blank=False)

让我们使用 shell 来检查一下:

注意以下事项:

  • 如果您使用多种语言,则使用 CI 字段会存在一些限制。使用前请阅读文档。

  • 要使用 CIText,您需要在数据库上创建扩展。您可以在 Django 迁移中完成此操作:

from django.db import migrations
from django.contrib.postgres.operations import CITextExtension

class Migration(migrations.Migration):

    dependencies = [
        ('storage', '0037_previous_migration'),
    ]

    operations = [
        CITextExtension(),
    ]

第二个选项:

为每个不区分大小写的字段创建 2 个字段。 第一个用于显示并保持文本的原始大小写。第二个将以小写形式保存,用于检查唯一性。

您的模型将如下所示:

class DemoModel(models.Model):


    class Meta(object):
        constraints = [
            UniqueConstraint(fields=['name_compare', 'app_compare'], name='unique_booking')
        ]

    name_display = models.TextField(null=False, default=None, blank=False, max_length=550)
    name_compare = models.TextField(null=False, default=None, blank=False, max_length=550)

    app_display = models.TextField(null=False, default=None, blank=False, max_length=550)
    app_compare = models.TextField(null=False, default=None, blank=False, max_length=550)

    def save(self, *args, **kwargs):
        # If explicitly received "compare" name - make sure it is casefolded
        if self.name_compare:
            self.name_compare = self.name_compare.casefold()
        # If didn't get "compare" name - use the display name and casefold it
        elif self.name_display:
            self.name_compare = self.name_display.casefold()

        # If explicitly received "compare" name - make sure it is casefolded
        if self.app_compare:
            self.app_compare = self.app_compare.casefold()
        # If didn't get "compare" name - use the display name and casefold it
        elif self.app_display:
            self.app_compare = self.app_display.casefold()

        return super(DemoModel, self).save(*args, **kwargs)

让我们使用 shell 来检查一下:

注意以下事项:

  • 阅读“案例折叠”与“下部”,以确定哪种更适合您的案例。

2
投票

根据此票,实现不区分大小写的唯一约束所需的这些更改已在 Django 代码库中实现(在 GitHub 中找到代码),并且代码当前为(截至 2021 年 4 月 29 日)在主分支中。但最新的 Django 版本3.2中没有这些变化。也许您应该能够在

Django >= 3.3
中使用它们。那么代码应该是这样的:

# not implemented in Django <= 3.2
models.UniqueConstraint(fields=[Lower('name'), Lower('app')], name='unique_booking', expression='')

同时,您可以尝试通过在保存时小写值来存储它们:

class YourModel():
    def save(self, *args, **kwargs):
        self.name = self.name.lower()
        self.app = self.app.lower()
        super().save(*args, **kwargs)

或者如果您使用的是 postgres 数据库,请考虑使用

CIText
字段。


2
投票

对于使用 Django 4.0 及更高版本的任何人,在 UniqueConstraint 表达式 的帮助下,您可以像这样向模型添加 Meta 类:

class Meta:
    constraints = [
        models.UniqueConstraint(
            'book',
            Lower('app'),
            name='unique_booking'
        ),
    ]

0
投票

对于 Django 版本 >= 4.0,这将起作用 https://docs.djangoproject.com/en/4.0/releases/4.0/#function-unique-constraints.

from django.db import models
from django.db.models import UniqueConstraint
from django.db.models.functions import Lower


class MyModel(models.Model):
    first_name = models.CharField(max_length=255)
    last_name = models.CharField(max_length=255)

    class Meta:
        constraints = [
            UniqueConstraint(
                Lower('first_name'),
                Lower('last_name').desc(),
                name='first_last_name_unique',
            ),
        ]

我尝试过,它对我有用。

© www.soinside.com 2019 - 2024. All rights reserved.