我基本上有一个用户名是唯一的(不区分大小写),但是在用户提供的显示时情况很重要。
我有以下要求:
Django有可能吗?
我提出的唯一解决方案是“以某种方式”覆盖模型管理器,使用额外字段,或者总是在搜索中使用“iexact”。
我在使用Django 1.3和PostgreSQL 8.4.2。
答案 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还提供CIEmailField
和CICharField
,它们是EmailField
和CharField
的不区分大小写的版本。
答案 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
。
第二种选择更加黑客和令人费解。我甚至犹豫不决,但为了完整起见,我会:覆盖filter
和get
,以便在用户名查询时忘记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()