Reputation: 1821
I would like to use this code:
constraints = [ models.UniqueConstraint(fields=['name', 'app'], name='unique_booking'), ]
but name and app (both of them) should not check case sensitive so "FOo" and "fOO" should be marked as the same app. How to do it?
Upvotes: 0
Views: 1885
Reputation: 81
For Django version >= 4.0, this will be work https://docs.djangoproject.com/en/4.0/releases/4.0/#functional-unique-constraints.
from django.db import models
from django.db.models import UniqueConstraint
from django.db.models.functions import Lower
class MyModel(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
class Meta:
constraints = [
UniqueConstraint(
Lower('first_name'),
Lower('last_name').desc(),
name='first_last_name_unique',
),
]
I tried and it is working for me.
Upvotes: 2
Reputation: 2539
For anyone using Django 4.0 and later, with the help of UniqueConstraint expressions you could add a Meta class to your model like this:
class Meta:
constraints = [
models.UniqueConstraint(
'book',
Lower('app'),
name='unique_booking'
),
]
Upvotes: 2
Reputation: 146
There are 2 options:
In case you are using postgresql and Django version which is later than 2.0 - you can use CICharField or CITextField fields.
class DemoModelCI(models.Model):
class Meta(object):
constraints = [
UniqueConstraint(fields=['app', 'name'], name='unique_booking_ci')
]
name = CICharField(null=False, default=None, max_length=550, blank=False)
app = CICharField(null=False, default=None, max_length=550, blank=False)
Working with the CI fields has some limitation if you work with multiple languages. Read documentation before using it.
To work with CIText you need to create the extension on your DB. You can do it in a Django migration:
from django.db import migrations
from django.contrib.postgres.operations import CITextExtension
class Migration(migrations.Migration):
dependencies = [
('storage', '0037_previous_migration'),
]
operations = [
CITextExtension(),
]
Create 2 fields for each of the case insensitive fields. The first will be for display and to maintain the original case of the text. The second will be saved lowercased and will be used for checking the uniqueness.
class DemoModel(models.Model):
class Meta(object):
constraints = [
UniqueConstraint(fields=['name_compare', 'app_compare'], name='unique_booking')
]
name_display = models.TextField(null=False, default=None, blank=False, max_length=550)
name_compare = models.TextField(null=False, default=None, blank=False, max_length=550)
app_display = models.TextField(null=False, default=None, blank=False, max_length=550)
app_compare = models.TextField(null=False, default=None, blank=False, max_length=550)
def save(self, *args, **kwargs):
# If explicitly received "compare" name - make sure it is casefolded
if self.name_compare:
self.name_compare = self.name_compare.casefold()
# If didn't get "compare" name - use the display name and casefold it
elif self.name_display:
self.name_compare = self.name_display.casefold()
# If explicitly received "compare" name - make sure it is casefolded
if self.app_compare:
self.app_compare = self.app_compare.casefold()
# If didn't get "compare" name - use the display name and casefold it
elif self.app_display:
self.app_compare = self.app_display.casefold()
return super(DemoModel, self).save(*args, **kwargs)
Upvotes: 3
Reputation: 51988
As per this ticket, these changes which are needed to implement case insensitive unique constaints have already been implemented in the Django codebase(found the codes in GitHub) and the code is currently(as of 29/04/2021) is in master branch. But the changes are not in latest Django version 3.2. Probably you should be able to use them in Django >= 3.3
. Then the code should be like this:
# not implemented in Django <= 3.2
models.UniqueConstraint(fields=[Lower('name'), Lower('app')], name='unique_booking', expression='')
In the mean time, you can either try to store them by lowercasing the values on save:
class YourModel():
def save(self, *args, **kwargs):
self.name = self.name.lower()
self.app = self.app.lower()
super().save(*args, **kwargs)
Or consider using CIText
field if you are using postgres database.
Upvotes: 3