Django中如何实现不区分大小写的唯一模型字段?
我有一个用户名,它是唯一的(不区分大小写),但在显示时要保留用户输入的大小写。
我有以下要求:
- 这个字段要和CharField兼容
- 这个字段要唯一,但不区分大小写
- 这个字段在搜索时要忽略大小写(避免使用iexact,因为容易忘记)
- 这个字段要保留用户输入的大小写
- 最好是在数据库层面强制执行
- 最好避免存储额外的字段
在Django中,这可能吗?
我想到的唯一解决方案是“某种方式”重写模型管理器,使用一个额外的字段,或者在搜索时总是使用'iexact'。
我使用的是Django 1.3和PostgreSQL 8.4.2。
11 个回答
截至2021年12月,借助于Django 4.0的UniqueConstraint表达式,你可以像这样在你的模型中添加一个Meta类:
class Meta:
constraints = [
models.UniqueConstraint(
Lower('<field name>'),
name='<constraint name>'
),
]
我并不是Django的专业开发者,对这个解决方案的性能等技术问题也不太了解。希望其他人能对此发表评论。
从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还提供了CIEmailField
和CICharField
,它们分别是不区分大小写的EmailField
和CharField
版本。
把原始的大小写混合字符串存放在一个普通文本列中。使用text
或者没有长度限制的varchar
类型,而不是varchar(n)
。这两者基本上是一样的,但使用varchar(n)时你需要设定一个任意的长度限制,如果以后想要更改就会很麻烦。想了解更多,可以查看手册或者Peter Eisentraut在serverfault.SE上的相关回答。
在lower(string)
上创建一个功能性唯一索引。这就是关键点:
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版本网站找到。