After a brief search, I see that this is a problem, and hopefully, it will be addressed by the Django team. See: https://code.djangoproject.com/ticket/34610
I have a workaround and it involves the use of the save method of Model.
First I wanted to use the clean method however somehow it did not work. Bu save is good enough.
Problem
Django raises an IntegrityError and redirects the error message of the database management system when a unique constraint happens. However, an IntegrityError can be risen for other errors too. So one needs to check the message of the error to see if the error is due to a unique constraint.
But different database management systems use different messages for unique constraints such as:
- Sqlite:
Error while executing SQL query on database '[The databse]': UNIQUE constraint failed: [The field]
- MariaDB:
SQL Error (1062): Duplicate entry '[The value]' for key '[The field]'
as you see finding a pattern for some common text looks like a challenge.
Solution
Override the save method of the Model and check if any other entry already exist on save.
Please notice save can be invoked when one creates a new entry or updates an entry.
Here I have a Countries model that would save list of available countries.
class Countries(models.Model):
created_at = models.DateTimeField(auto_created=True, auto_now_add=True)
name = models.CharField(max_length=128, null=False, blank=False)
code = models.CharField(max_length=128, null=False, blank=False, unique=True)
timezone = models.CharField(max_length=128, null=False, blank=False)
def __str__(self):
return self.name
def save(
self, force_insert=False, force_update=False, using=None, update_fields=None
):
# To check unique constraint.
try:
country_with_the_same_code = Countries.objects.get(code=self.code)
# Get a country that has the code (must be unique)
# If there is no such country, DoesNotExist exception will be risen,
# and the next lines will be skipped.
# Check if self.pk is not equal to the existing country's pk with the
# same code
# If it is not equal, It means someone is trying to create a new country
# or update a country, and the code is equal to some other entries code.
# (Raise an error)
# if it is equal, it means someone is trying to update a country with the same
# code of itself. (No error)
if self.pk != country_with_the_same_code.pk:
raise ValidationError("Unique Constraint failed for code")
except Countries.DoesNotExist:
# If no country is available with the same code (must be unique)
# do nothing
pass
# Just execute save method of super.
super(Countries, self).save(force_insert=force_insert, force_update=force_update, using=using,
update_fields=update_fields)
I over-commented the save method, just because I am lazy to explain it here...
And here how one can use it:
try:
the_country = Countries(name=name, code=code, timezone=timezone)
the_country.save()
except ValidationError as e:
if "Unique Constraint failed" in str(e):
print("Not unique")
Update
Creating a custom Exception such as Unique instead of ValidationError would be a better approach. That way you do not need to check the message and the usage would change to be as such:
try:
the_country = Countries(name=name, code=code, timezone=timezone)
the_country.save()
except Unique:
print("Not unique")
exceptclause.