J'ai essentiellement un nom d'utilisateur est unique (insensible à la casse), mais la casse est importante lors de l'affichage tel que fourni par l'utilisateur.
J'ai les exigences suivantes:
Est-ce possible à Django?
La seule solution que j'ai proposée est «en quelque sorte» de remplacer le gestionnaire de modèles, d'utiliser un champ supplémentaire ou de toujours utiliser «iexact» dans les recherches.
Je suis sur Django 1.3 et PostgreSQL 8.4.2.
Stockez la chaîne originale à la casse mixte dans une colonne de texte brut . Utilisez le type de donnéestext
ou varchar
sans modificateur de longueur plutôt que varchar(n)
. Ils sont essentiellement les mêmes, mais avec varchar (n), vous devez définir une limite de longueur arbitraire, ce qui peut être difficile si vous souhaitez modifier ultérieurement. En savoir plus sur cela dans le manuel ou dans ce réponse associée de Peter Eisentraut @ serverfault.SE .
Créez un index unique fonctionnel sur lower(string)
. C'est le point majeur ici:
CREATE UNIQUE INDEX my_idx ON mytbl(lower(name));
Si vous essayez de INSERT
un nom de casse mixte qui existe déjà en minuscule, vous obtenez une erreur de violation de clé unique.
Pour les recherches d'égalité rapides, utilisez une requête comme celle-ci:
SELECT * FROM mytbl WHERE lower(name) = 'foo' --'foo' is lower case, of course.
Utilisez la même expression que vous avez dans l'index (pour que le planificateur de requêtes reconnaisse la compatibilité) et ce sera très rapide.
En passant: vous voudrez peut-être passer à une version plus récente de PostgreSQL. Il y a eu beaucoup de corrections importantes depuis la 8.4.2 . Plus d'informations sur le site officiel de gestion de versions Postgres .
Si vous remplacez le gestionnaire de modèles, vous avez deux options. Tout d'abord, il suffit de créer une nouvelle méthode de recherche:
class MyModelManager(models.Manager):
def get_by_username(self, username):
return self.get(username__iexact=username)
class MyModel(models.Model):
...
objects = MyModelManager()
Ensuite, vous utilisez get_by_username('blah')
au lieu de get(username='blah')
et vous n'avez pas à vous soucier d'oubli iexact
. Bien sûr, cela nécessite que vous vous souveniez d'utiliser get_by_username
.
La deuxième option est beaucoup plus compliquée et compliquée. J'hésite même à le suggérer, mais par souci d'exhaustivité, je vais: remplacer filter
et get
de telle sorte que si vous oubliez iexact
lorsque vous interrogez par nom d'utilisateur, il l'ajoutera à votre place.
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()
À partir de Django 1.11, vous pouvez utiliser CITextField , un champ spécifique à Postgres pour le texte ne respectant pas la casse et sauvegardé par le type citext.
from Django.db import models
from Django.contrib.postgres.fields import CITextField
class Something(models.Model):
foo = CITextField()
Django fournit également CIEmailField
et CICharField
, qui sont des versions de EmailField
et CharField
qui respectent la casse.
Vous pouvez utiliser le type postgres citext à la place et ne plus vous embêter avec aucune sorte d’exacte. Il suffit de noter dans le modèle que le champ sous-jacent n’est pas sensible à la casse. Solution beaucoup plus simple.
Comme un nom d'utilisateur est toujours en minuscule, il est recommandé d'utiliser un champ de modèle en minuscule personnalisé dans Django. Pour faciliter l’accès et ranger le code, créez un nouveau fichier fields.py
dans votre dossier d’application.
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
Utilisation 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)
Note de fin : Vous pouvez utiliser cette méthode pour stocker des valeurs minuscules dans la base de données sans vous soucier de __iexact
.
Vous pouvez utiliser lookup = 'iexact' dans UniqueValidator sur le sérialiseur, comme suit: Champ de modèle unique dans Django et sensibilité à la casse (postgres)
La meilleure solution consiste à remplacer "get_prep_value" par Django Models Field
class LowerSlugField(models.SlugField): def get_prep_value(self, value): return str(value).lower()
et alors:
company_slug = LowerSlugField(max_length=255, unique=True)