31π
Store the original mixed-case string in a plain text column. Use the data type text
or varchar
without length modifier rather than varchar(n)
. They are essentially the same, but with varchar(n) you have to set an arbitrary length limit, that can be a pain if you want to change later. Read more about that in the manual or in this related answer by Peter Eisentraut @serverfault.SE.
Create a functional unique index on lower(string)
. Thatβs the major point here:
CREATE UNIQUE INDEX my_idx ON mytbl(lower(name));
If you try to INSERT
a mixed case name thatβs already there in lower case you get a unique key violation error.
For fast equality searches use a query like this:
SELECT * FROM mytbl WHERE lower(name) = 'foo' --'foo' is lower case, of course.
Use the same expression you have in the index (so the query planner recognizes the compatibility) and this will be very fast.
As an aside: you may want to upgrade to a more recent version of PostgreSQL. There have been lots of important fixes since 8.4.2. More on the official Postgres versioning site.
47π
As of Django 1.11, you can use CITextField, a Postgres-specific Field for case-insensitive text backed by the citext type.
from django.db import models
from django.contrib.postgres.fields import CITextField
class Something(models.Model):
foo = CITextField()
Django also provides CIEmailField
and CICharField
, which are case-insensitive versions of EmailField
and CharField
.
- [Django]-Warning: Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'
- [Django]-MySQL "incorrect string value" error when save unicode string in Django
- [Django]-"<Message: title>" needs to have a value for field "id" before this many-to-many relationship can be used.
30π
As of December 2021, with the help of Django 4.0 UniqueConstraint expressions you can add a Meta class to your model like this:
class Meta:
constraints = [
models.UniqueConstraint(
Lower('<field name>'),
name='<constraint name>'
),
]
Iβm by no mean a Django professional developer and I donβt know technical considerations like performance issues about this solution. Hope others comment on that.
- [Django]-Extend base.html problem
- [Django]-Inject errors into already validated form?
- [Django]-Django models: get list of id
20π
With overriding the model manager, you have two options. First is to just create a new lookup method:
class MyModelManager(models.Manager):
def get_by_username(self, username):
return self.get(username__iexact=username)
class MyModel(models.Model):
...
objects = MyModelManager()
Then, you use get_by_username('blah')
instead of get(username='blah')
, and you donβt have to worry about forgetting iexact
. Of course that then requires that you remember to use get_by_username
.
The second option is much hackier and convoluted. Iβm hesitant to even suggest it, but for completeness sake, I will: override filter
and get
such that if you forget iexact
when querying by username, it will add it for you.
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]-Why does DEBUG=False setting make my django Static Files Access fail?
- [Django]-Django: Does prefetch_related() follow reverse relationship lookup?
- [Django]-Django add extra field to a ModelForm generated from a Model
6π
Since a username is always lowercase, itβs recommended to use a custom lowercase model field in Django. For the ease of access and code-tidiness, create a new file fields.py
in your app folder.
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
Usage 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 : You can use this method to store lowercase values in the database, and not worry about __iexact
.
- [Django]-How to write setup.py to include a Git repository as a dependency
- [Django]-Best practices for adding .gitignore file for Python projects?
- [Django]-Django-reversion and related model
3π
You can use citext postgres type instead and not bother anymore with any sort of iexact. Just make a note in model that underlying field is case insensitive.
Much easier solution.
- [Django]-Django error β matching query does not exist
- [Django]-How would you create a 'manual' django migration?
- [Django]-'RelatedManager' object is not iterable Django
1π
You can use lookup=βiexactβ in UniqueValidator on serializer, like this:
Unique model field in Django and case sensitivity (postgres)
- [Django]-Django + Ajax
- [Django]-Generating a Random Hex Color in Python
- [Django]-Django β is not a registered namespace
1π
I liked Chris Prattβs Answer but it didnβt worked for me, because the models.Manager
-class doesnβt have the get(...)
or filter(...)
Methods.
I had to take an extra step via a custom QuerySet
:
from django.contrib.auth.base_user import BaseUserManager
from django.db.models import QuerySet
class CustomUserManager(BaseUserManager):
# Use the custom QuerySet where get and filter will change 'email'
def get_queryset(self):
return UserQuerySet(self.model, using=self._db)
def create_user(self, email, password, **extra_fields):
...
def create_superuser(self, email, password, **extra_fields):
...
class UserQuerySet(QuerySet):
def filter(self, *args, **kwargs):
if 'email' in kwargs:
# Probably also have to replace...
# email_contains -> email_icontains,
# email_exact -> email_iexact,
# etc.
kwargs['email__iexact'] = kwargs['email']
del kwargs['email']
return super().filter(*args, **kwargs)
def get(self, *args, **kwargs):
if 'email' in kwargs:
kwargs['email__iexact'] = kwargs['email']
del kwargs['email']
return super().get(*args, **kwargs)
This worked for me in a very simple case but is working pretty good so far.
- [Django]-How to handle request.GET with multiple variables for the same parameter in Django
- [Django]-Django test app error β Got an error creating the test database: permission denied to create database
- [Django]-What is a NoReverseMatch error, and how do I fix it?
0π
You can also override get_prep_value()
and reuse it through inheritance.
class LowerCaseField:
def get_prep_value(self, value):
value = super().get_prep_value(value)
if value:
value = value.strip().lower()
return value
class LowerSlugField(LowerCaseField, models.SlugField):
pass
class LowerEmailField(LowerCaseField, models.EmailField):
pass
class MyModel(models.Model):
email = LowerEmailField(max_length=255, unique=True)
This way, if you ever want to reuse this field in another model, you can use the same consistent strategy.
From Django Docs:
get_prep_value
(value)
value is the current value of the modelβs
attribute, and the method should return data in a format that has been
prepared for use as a parameter in a query.See Converting Python objects to query values for usage.
- [Django]-Class Based Views VS Function Based Views
- [Django]-How to drop all tables from the database with manage.py CLI in Django?
- [Django]-Aggregate() vs annotate() in Django
0π
one of the best options for do this is that create a form and past it exept of the admin form .
like this :
from . Import YourModel
from django import forms
from django.contrib import admin
class HandMadeFormForAdminPage(forms.ModelForm):
class Meta:
model = YourModel
fields = "__all__"
def clean(self):
cleaned_data = super().clean()
field_value = self.cleaned_data.get("YourFieldName")
value_pair = YourModel.objects.filter(YourModelFieldName__iexact=field_value).first()
if value_pair:
self.add_error("YourFieldName", f"the {value_pair} alredy exist")
class YourModelAdmin(admin.ModelAdmin):
form = HandMadeFormForAdminPage
- [Django]-Fighting client-side caching in Django
- [Django]-Django Rest Framework remove csrf
- [Django]-How to change empty_label for modelForm choice field?
0π
by using def db_type you could also do something like:
from django.db import models
from django.utils.translation import gettext_lazy as _
class CaseInsensitiveCharField(models.CharField):
description = _("Case insensitive character")
def db_type(self, connection):
return "citext"
- [Django]-How to handle request.GET with multiple variables for the same parameter in Django
- [Django]-How to use UUID
- [Django]-How do I clone a Django model instance object and save it to the database?