Django中的独特模型字段和区分大小写(postgres)

che*_*art 26 django postgresql unique django-models

考虑以下情况: -

假设我的应用程序允许用户在其国家/地区创建州/省.为了清楚起见,我们在这里只考虑ASCII字符.

在美国,用户可以创建名为"Texas"的州.如果这个应用程序在内部使用,让我们说用户不关心拼写为"texas"或"Texas"或"teXas"

但重要的是,如果数据库中已存在"Texas",则系统应阻止创建"texas".

如果模型如下所示:

class State(models.Model):
    name = models.CharField(max_length=50, unique=True)
Run Code Online (Sandbox Code Playgroud)

postgres中的唯一性将区分大小写; 也就是说,postgres将允许用户创建"texas"和"Texas",因为它们被认为是唯一的.

在这种情况下可以做些什么来防止这种行为.一个人如何去提供区分insenstitive独特性和Django和Postgres

现在我正在执行以下操作以防止创建不区分大小写的重复项.

class CreateStateForm(forms.ModelForm):
    def clean_name(self):
        name = self.cleaned_data['name']
        try:
            State.objects.get(name__iexact=name)
        except ObjectDoesNotExist:
            return name
        raise forms.ValidationError('State already exists.')

    class Meta:
        model = State
Run Code Online (Sandbox Code Playgroud)

在许多情况下,我将不得不进行此项检查,而且我并不热衷于必须在任何地方编写类似的iexact检查.

只是想知道是否有内置或更好的方式?也许db_type会有帮助吗?也许存在其他一些解决方案

May*_*esh 29

您可以定义派生自的自定义模型字段models.CharField.此字段可以检查重复值,忽略大小写.

自定义字段文档在这里http://docs.djangoproject.com/en/dev/howto/custom-model-fields/

请查看http://code.djangoproject.com/browser/django/trunk/django/db/models/fields/files.py,了解如何通过继承现有字段来创建自定义字段的示例.

您可以使用PostgreSQL的citext模块https://www.postgresql.org/docs/current/static/citext.html

如果使用此模块,则自定义字段可以将"db_type"定义为PostgreSQL数据库的CITEXT.

这将导致对自定义字段中的唯一值进行不区分大小写的比较.


小智 7

或者,您可以更改默认的查询集管理器,以对字段执行不区分大小写的查找.在试图解决类似问题时,我遇到了:

http://djangosnippets.org/snippets/305/

为方便起见,此处粘贴代码:

from django.db.models import Manager
from django.db.models.query import QuerySet

class CaseInsensitiveQuerySet(QuerySet):
    def _filter_or_exclude(self, mapper, *args, **kwargs):
        # 'name' is a field in your Model whose lookups you want case-insensitive by default
        if 'name' in kwargs:
            kwargs['name__iexact'] = kwargs['name']
            del kwargs['name']
        return super(CaseInsensitiveQuerySet, self)._filter_or_exclude(mapper, *args, **kwargs)

# custom manager that overrides the initial query set
class TagManager(Manager):
    def get_query_set(self):
        return CaseInsensitiveQuerySet(self.model)

# and the model itself
class Tag(models.Model):
    name = models.CharField(maxlength=50, unique=True, db_index=True)

    objects = TagManager()

    def __str__(self):
        return self.name
Run Code Online (Sandbox Code Playgroud)


Ale*_*vik 6

在Postgres方面,功能唯一索引将允许您在没有大小写的情况下强制执行唯一值.还注意到citext,但这适用于较旧版本的PostgreSQL,并且通常是一种有用的技术.

例:

# create table foo(bar text);
CREATE TABLE
# create unique index foo_bar on foo(lower(bar));
CREATE INDEX
# insert into foo values ('Texas');
INSERT 0 1
# insert into foo values ('texas');
ERROR:  duplicate key value violates unique constraint "foo_bar"
Run Code Online (Sandbox Code Playgroud)

  • 那么,您应该始终在数据库中强制执行约束. (2认同)

suh*_*lvs 6

一个非常简单的解决方案:

class State(models.Model):
    name = models.CharField(max_length=50, unique=True)

    def clean(self):
        self.name = self.name.capitalize()
Run Code Online (Sandbox Code Playgroud)


eya*_*ler 6

Mayuresh的答案明确的步骤:

  1. 在postgres做:CREATE EXTENSION citext;

  2. 在你的models.py中添加:

    from django.db.models import fields
    
    class CaseInsensitiveTextField(fields.TextField):
        def db_type(self, connection):
            return "citext"
    
    Run Code Online (Sandbox Code Playgroud)

    参考:https://github.com/zacharyvoase/django-postgres/blob/master/django_postgres/citext.py

  3. 在您的模型中使用:name = CaseInsensitiveTextField(unique = True)


Mic*_*hař 5

除了已经提到的覆盖保存选项之外,您可以简单地将所有文本以小写形式存储在数据库中并在显示时将它们大写。

class State(models.Model):
    name = models.CharField(max_length=50, unique=True)

    def save(self, force_insert=False, force_update=False):
        self.name = self.name.lower()
        super(State, self).save(force_insert, force_update)
Run Code Online (Sandbox Code Playgroud)