基本的にユーザー名は一意です(大文字と小文字は区別されません)が、ユーザーが指定したとおりに表示する場合は大文字と小文字が区別されます。
次の要件があります。
これはDjangoで可能ですか?
私が思いついた唯一の解決策は、「どういうわけか」モデルマネージャーをオーバーライドするか、追加のフィールドを使用するか、検索で常に「iexact」を使用することです。
私はDjango 1.3とPostgreSQL 8.4.2を使用しています。
元の大文字と小文字が混在する文字列をプレーンテキスト列に格納します。データ型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バージョン管理サイト をご覧ください。
Django 1.11以降、 CITextField を使用できます。これは、citextタイプによってサポートされる、大文字と小文字を区別しないテキスト用のPostgres固有のフィールドです。
from Django.db import models
from Django.contrib.postgres.fields import CITextField
class Something(models.Model):
foo = CITextField()
DjangoはCIEmailField
とCICharField
も提供します。これらはEmailField
とCharField
の大文字と小文字を区別しないバージョンです。
Model Managerをオーバーライドする場合、2つのオプションがあります。まず、新しいルックアップメソッドを作成するだけです。
_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
_を使用することを忘れないようにする必要があります。
2番目のオプションははるかにハッカーで複雑です。私はそれを提案することもためらっていますが、完全を期すために、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()
_
ユーザー名は常に小文字なので、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
使用方法 in 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)
End Note:このメソッドを使用して、小文字の値をデータベースに格納できます。__iexact
。
代わりにcitext postgresタイプを使用することができ、iexactの種類を気にする必要はもうありません。基になるフィールドでは大文字と小文字が区別されないことをモデルにメモしてください。はるかに簡単な解決策。
次のように、シリアライザのUniqueValidatorでlookup = 'iexact'を使用できます。 Djangoの一意のモデルフィールドと大文字と小文字の区別(postgres)
最善の解決策は、「get_prep_value」をDjangoモデルフィールドでオーバーライドすることです。
class LowerSlugField(models.SlugField): def get_prep_value(self, value): return str(value).lower()
その後:
company_slug = LowerSlugField(max_length=255, unique=True)