web-dev-qa-db-ja.com

Djangoの大文字と小文字を区別しない一意のモデルフィールド?

基本的にユーザー名は一意です(大文字と小文字は区別されません)が、ユーザーが指定したとおりに表示する場合は大文字と小文字が区別されます。

次の要件があります。

  • フィールドはCharField互換です
  • フィールドは一意ですが、大文字と小文字は区別されません
  • フィールドは大文字小文字を無視して検索可能である必要があります(iexactの使用は避け、簡単に忘れられる)
  • フィールドは大文字と小文字を区別せずに保存されます
  • できればデータベースレベルで適用
  • できれば余分なフィールドを保存しないでください

これはDjangoで可能ですか?

私が思いついた唯一の解決策は、「どういうわけか」モデルマネージャーをオーバーライドするか、追加のフィールドを使用するか、検索で常に「iexact」を使用することです。

私はDjango 1.3とPostgreSQL 8.4.2を使用しています。

47
noobzie

元の大文字と小文字が混在する文字列をプレーンテキスト列に格納します。データ型textまたはvarcharvarchar(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バージョン管理サイト をご覧ください。

26

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はCIEmailFieldCICharFieldも提供します。これらはEmailFieldCharFieldの大文字と小文字を区別しないバージョンです。

23
Rodney Folz

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番目のオプションははるかにハッカーで複雑です。私はそれを提案することもためらっていますが、完全を期すために、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()
_
17
Chris Pratt

ユーザー名は常に小文字なので、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

3
Suraj

代わりにcitext postgresタイプを使用することができ、iexactの種類を気にする必要はもうありません。基になるフィールドでは大文字と小文字が区別されないことをモデルにメモしてください。はるかに簡単な解決策。

3
Zorg

次のように、シリアライザの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)
0
NKSM