Django中不区分大小写的独特模型字段?

时间:2011-10-14 20:41:12

标签: python django postgresql model

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

我有以下要求:

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

Django有可能吗?

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

我在使用Django 1.3和PostgreSQL 8.4.2。

7 个答案:

答案 0 :(得分:26)

将原始混合大小写字符串存储在纯文本列中。使用数据类型 text varchar而不使用长度修饰符而不是varchar(n)。它们本质上是相同的,但是使用varchar(n)你必须设置一个任意长度限制,如果你想稍后改变,这可能会很痛苦。详细了解in the manual或此related answer by Peter Eisentraut @serverfault.SE

lower(string)上创建functional unique index。这是主要观点:

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

如果您尝试INSERT小写的已混合大小写的名称,则会出现唯一的密钥违例错误。
对于快速相等搜索,请使用如下查询:

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

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


暂且不说:您可能希望升级到更新版本的PostgreSQL。有很多important fixes since 8.4.2。有关official Postgres versioning site的更多信息。

答案 1 :(得分:19)

从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的不区分大小写的版本。

答案 2 :(得分:17)

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

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')代替get(username='blah'),并且您不必担心忘记iexact。当然,那要求你记得使用get_by_username

第二种选择更加黑客和令人费解。我甚至犹豫不决,但为了完整起见,我会:覆盖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()

答案 3 :(得分:3)

您可以使用citext postgres类型而不再烦扰任何类型的iexact。只需在模型中记下底层字段不区分大小写。 更简单的解决方案。

答案 4 :(得分:3)

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

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
models.py

中的

用法

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

答案 5 :(得分:1)

您可以使用lookup =' iexact'在序列化器上的UniqueValidator中,如下所示: Unique model field in Django and case sensitivity (postgres)

答案 6 :(得分:0)

最好的解决方案是通过Django模型字段覆盖“ get_prep_value”

xyz

然后:

class LowerSlugField(models.SlugField):

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