python-Django中不区分大小写的唯一模型字段?

我的用户名基本上是唯一的(不区分大小写),但是按用户提供的显示时大小写很重要。

我有以下要求:

  • 字段与CharField兼容
  • 字段是唯一的,但不区分大小写
  • 字段需要可忽略大小写进行搜索(避免使用iexact,容易忘记)
  • 字段存储完整的情况
  • 最好在数据库级别执行
  • 最好避免存储额外的字段

这在Django中可能吗?

我想出的唯一解决方案是“以某种方式”覆盖模型管理器,使用额外的字段或在搜索中始终使用“ iexact”。

我使用的是Django 1.3和PostgreSQL 8.4.2。

noobzie asked 2020-08-05T07:08:01Z
7个解决方案
26 votes

将原始大小写混合的字符串存储在纯文本列中。 使用没有长度修饰符而不是varchar(n)的数据类型INSERTINSERTINSERT。它们本质上是相同的,但是对于varchar(n),您必须设置一个任意长度限制,如果以后要更改,可能会很麻烦。 在手册中或在Peter Eisentraut @ serverfault.SE的相关答案中了解更多有关此内容的信息。

INSERT上创建一个功能唯一索引。这是这里的要点:

CREATE UNIQUE INDEX my_idx ON mytbl(lower(name));

如果尝试使用INSERT混合大小写名称(小写字母已经存在),则会出现唯一的密钥冲突错误。
对于快速相等搜索,请使用如下查询:

SELECT * FROM mytbl WHERE lower(name) = 'foo' --'foo' is lower case, of course.

使用您在索引中使用的相同表达式(以便查询计划程序识别兼容性),这将非常快。


顺便说一句:您可能想升级到PostgreSQL的最新版本。 自8.4.2以来,已经有很多重要的修复程序。 更多有关官方Postgres版本控制网站的信息。

Erwin Brandstetter answered 2020-08-05T07:08:55Z
24 votes

从Django 1.11开始,您可以使用CITextField,这是Postgres专用的字段,用于由citext类型支持的不区分大小写的文本。

from django.db import models
from django.contrib.postgres.fields import CITextField

class Something(models.Model):
    foo = CITextField()

Django还提供了CIEmailFieldCICharField,它们是EmailFieldCharField的不区分大小写的版本。

Rodney Folz answered 2020-08-05T07:09:19Z
17 votes

通过覆盖模型管理器,您有两个选择。 首先是创建一个新的查找方法:

class MyModelManager(models.Manager):
   def get_by_username(self, username):
       return self.get(username__iexact=username)

class MyModel(models.Model):
   ...
   objects = MyModelManager()

然后,您使用get_by_username('blah')而不是filter,而不必担心忘记get。当然,这要求您记住使用iexact

第二种选择是更复杂和复杂。 我什至不愿提出建议,但出于完整性考虑,我将:覆盖filterget,这样,如果您在按用户名查询时忘记了iexact,它将为您添加它。

class MyModelManager(models.Manager):
    def filter(self, **kwargs):
        if 'username' in kwargs:
            kwargs['username__iexact'] = kwargs['username']
            del kwargs['username']
        return super(MyModelManager, self).filter(**kwargs)

    def get(self, **kwargs):
        if 'username' in kwargs:
            kwargs['username__iexact'] = kwargs['username']
            del kwargs['username']
        return super(MyModelManager, self).get(**kwargs)

class MyModel(models.Model):
   ...
   objects = MyModelManager()
Chris Pratt answered 2020-08-05T07:09:48Z
3 votes

您可以改用citext postgres类型,而不再受任何iexact的困扰。 只需在模型中注明基础字段不区分大小写即可。解决方案简单得多。

Zorg answered 2020-08-05T07:10:09Z
3 votes

由于用户名始终是小写的,因此建议在Django中使用自定义的小写模型字段。 为了便于访问和整理代码,请在您的应用文件夹中创建一个新文件__iexact

from django.db import models
from django.utils.six import with_metaclass

# Custom lowecase CharField

class LowerCharField(with_metaclass(models.SubfieldBase, models.CharField)):
    def __init__(self, *args, **kwargs):
        self.is_lowercase = kwargs.pop('lowercase', False)
        super(LowerCharField, self).__init__(*args, **kwargs)

    def get_prep_value(self, value):
        value = super(LowerCharField, self).get_prep_value(value)
        if self.is_lowercase:
            return value.lower()
        return value

__iexact中的用途

from django.db import models
from your_app_name.fields import LowerCharField

class TheUser(models.Model):
    username = LowerCharField(max_length=128, lowercase=True, null=False, unique=True)

结束语:可以使用此方法在数据库中存储小写值,而不必担心__iexact

Suraj answered 2020-08-05T07:10:37Z
1 votes

您可以在序列化程序的UniqueValidator中使用lookup ='iexact',如下所示:Django中的唯一模型字段和区分大小写(postgres)

Luan Francisco Lima Fernandes answered 2020-08-05T07:10:58Z
0 votes

最好的解决方案是通过Django Models Field覆盖“ get_prep_value”

class LowerSlugField(models.SlugField):

    def get_prep_value(self, value):
        return str(value).lower()

然后:

company_slug = LowerSlugField(max_length=255, unique=True)
NKSM answered 2020-08-05T07:11:22Z
translate from https://stackoverflow.com:/questions/7773341/case-insensitive-unique-model-fields-in-django