32
You could define a custom model field derived from models.CharField
.
This field could check for duplicate values, ignoring the case.
Custom fields documentation is here http://docs.djangoproject.com/en/dev/howto/custom-model-fields/
Look at http://code.djangoproject.com/browser/django/trunk/django/db/models/fields/files.py for an example of how to create a custom field by subclassing an existing field.
You could use the citext module of PostgreSQL https://www.postgresql.org/docs/current/static/citext.html
If you use this module, the the custom field could define โdb_typeโ as CITEXT for PostgreSQL databases.
This would lead to case insensitive comparison for unique values in the custom field.
8
Alternatively you can change the default Query Set Manager to do case insensitive look-ups on the field. In trying to solve a similar problem I came across:
http://djangosnippets.org/snippets/305/
Code pasted here for convenience:
from django.db.models import Manager
from django.db.models.query import QuerySet
class CaseInsensitiveQuerySet(QuerySet):
def _filter_or_exclude(self, mapper, *args, **kwargs):
# 'name' is a field in your Model whose lookups you want case-insensitive by default
if 'name' in kwargs:
kwargs['name__iexact'] = kwargs['name']
del kwargs['name']
return super(CaseInsensitiveQuerySet, self)._filter_or_exclude(mapper, *args, **kwargs)
# custom manager that overrides the initial query set
class TagManager(Manager):
def get_query_set(self):
return CaseInsensitiveQuerySet(self.model)
# and the model itself
class Tag(models.Model):
name = models.CharField(maxlength=50, unique=True, db_index=True)
objects = TagManager()
def __str__(self):
return self.name
- [Django]-Caching query results in django
- [Django]-Django: no such table: django_session
- [Django]-When saving, how can you check if a field has changed?
7
a very simple solution:
class State(models.Model):
name = models.CharField(max_length=50, unique=True)
def clean(self):
self.name = self.name.capitalize()
- [Django]-SQLAlchemy Model Django like Save Method?
- [Django]-Django โ getlist()
- [Django]-"Disabled" option for choiceField โ Django
7
Explicit steps for Mayureshโs answer:
-
in postgres do: CREATE EXTENSION citext;
-
in your models.py add:
from django.db.models import fields class CaseInsensitiveTextField(fields.TextField): def db_type(self, connection): return "citext"
reference: https://github.com/zacharyvoase/django-postgres/blob/master/django_postgres/citext.py
-
in your model use: name = CaseInsensitiveTextField(unique=True)
- [Django]-Django view โ load template from calling app's dir first
- [Django]-In a Django QuerySet, how to filter for "not exists" in a many-to-one relationship
- [Django]-Execute code in Django after response has been sent to the client
6
On the Postgres side of things, a functional unique index will let you enforce unique values without case. citext is also noted, but this will work with older versions of PostgreSQL and is a useful technique in general.
Example:
# create table foo(bar text);
CREATE TABLE
# create unique index foo_bar on foo(lower(bar));
CREATE INDEX
# insert into foo values ('Texas');
INSERT 0 1
# insert into foo values ('texas');
ERROR: duplicate key value violates unique constraint "foo_bar"
- [Django]-In django do models have a default timestamp field?
- [Django]-Determine complete Django url configuration
- [Django]-Django, template context processors
5
Besides already mentioned option to override save, you can simply store all text in lower case in database and capitalize them on displaying.
class State(models.Model):
name = models.CharField(max_length=50, unique=True)
def save(self, force_insert=False, force_update=False):
self.name = self.name.lower()
super(State, self).save(force_insert, force_update)
- [Django]-Django populate() isn't reentrant
- [Django]-Django REST Framework โ Serializing optional fields
- [Django]-Do properties work on Django model fields?
3
You can use lookup=โiexactโ in UniqueValidator on serializer, like this:
class StateSerializer(serializers.ModelSerializer):
name = serializers.CharField(validators=[
UniqueValidator(
queryset=models.State.objects.all(),lookup='iexact'
)]
django version: 1.11.6
- [Django]-Access-Control-Allow-Origin in Django app
- [Django]-Why middleware mixin declared in django.utils.deprecation.py
- [Django]-Django-Rest-Framework 3.0 Field name '<field>' is not valid for model `ModelBase`
2
If you donโt want to use a postgres-specific solution, you can create a unique index on the field with upper()
to enforce uniqueness at the database level, then create a custom Field
mixin that overrides get_lookup()
to convert case-sensitive lookups to their case-insensitive versions. The mixin looks like this:
class CaseInsensitiveFieldMixin:
"""
Field mixin that uses case-insensitive lookup alternatives if they exist.
"""
LOOKUP_CONVERSIONS = {
'exact': 'iexact',
'contains': 'icontains',
'startswith': 'istartswith',
'endswith': 'iendswith',
'regex': 'iregex',
}
def get_lookup(self, lookup_name):
converted = self.LOOKUP_CONVERSIONS.get(lookup_name, lookup_name)
return super().get_lookup(converted)
And you use it like this:
from django.db import models
class CICharField(CaseInsensitiveFieldMixin, models.CharField):
pass
class CIEmailField(CaseInsensitiveFieldMixin, models.EmailField):
pass
class TestModel(models.Model):
name = CICharField(unique=True, max_length=20)
email = CIEmailField(unique=True)
You can read more about this approach here.
- [Django]-Django REST Framework: raise error when extra fields are present on POST
- [Django]-How does Django's nested Meta class work?
- [Django]-How to monkey patch Django?
1
You can do this by overwriting the Modelโs save method โ see the docs. Youโd basically do something like:
class State(models.Model):
name = models.CharField(max_length=50, unique=True)
def save(self, force_insert=False, force_update=False):
if State.objects.get(name__iexact = self.name):
return
else:
super(State, self).save(force_insert, force_update)
Also, I may be wrong about this, but the upcoming model-validation SoC branch will allow us to do this more easily.
- [Django]-How to use custom AdminSite class?
- [Django]-What's the difference between Model.id and Model.pk in django?
- [Django]-How can I obtain the model's name or the content type of a Django object?
0
Solution from suhail worked for me without the need to enable citext, pretty easy solution only a clean function and instead of capitalize I used upper()
. Mayureshโs solution also works but changed the field from CharField
to TextField
.
class State(models.Model):
name = models.CharField(max_length=50, unique=True)
def clean(self):
self.name = self.name.upper()
- [Django]-Django Generic Views using decorator login_required
- [Django]-How to use curl with Django, csrf tokens and POST requests
- [Django]-Update all models at once in Django