77👍
from django.db import IntegrityError
except IntegrityError:
This is what you need.
EDITED for @mbrochh:
from django.db import IntegrityError
except IntegrityError as e:
if 'unique constraint' in e.message: # or e.args[0] from Django 1.10
#do something
Yes, you can be more precise but in question case UNIQUE failed
is highly likely.
27👍
IMHO, I would recommend to resolve this situation by get_or_create().
new_obj, created = AnswerModel.objects.get_or_create(user=user, yes_question=question_model)
if created:
do_something_for_new_object(new_obj)
else:
logging.error("Duplicated item.")
return
- [Django]-Django: How to manage development and production settings?
- [Django]-How do I display the Django '__all__' form errors in the template?
- [Django]-Django development IDE
10👍
Usually the “ask for forgiveness” principle is a good practice in programming but in this special case, I would not recommend it.
The exception you are looking for is IntegrityError
. You could have easily figured that out yourself by simply removing the try-catch block and forcing that exception. The traceback shows the exception class.
The problem is, there are several different kinds of integrity errors, so inside your try-catch block you would have to check for something like if ex.pgcode == 23505
to see if this is actually a UNIQUE constraint error. This has been answered before here: IntegrityError: distinguish between unique constraint and not null violations
It gets worse: Each ORM has different error codes, the field name will not be pgcode
but something else and some ORMs don’t throw UNIQUE constraints at all. So if you are building a reusable app or if you are using a ORM that sucks (such as MySQL) or if you are not sure if you will change the database of your project at some time in the future, you should not do this!
The better way is simply removing the try-catch block and check if the object is already in the database before saving.
I don’t know which field is UNIQUE in your case, so I will just assume that it is the user
field. Your code would look something like this:
answers = AnswerModel.objects.filter(user=user)
if answers:
return HttpResponseRedirect('/user/already_exists')
obj = AnswerModel.objects.create(user=user, yes_question=question_model)
...
If you are dealing with a combined unique constraint, the first line would be this:
answers = AnswerModel.objects.filter(user=user, yes_question=question_model)
- [Django]-Change model class name in Django admin interface
- [Django]-Django 1.10.1 'my_templatetag' is not a registered tag library. Must be one of:
- [Django]-Difference between auto_now and auto_now_add
4👍
For Python > 3.5
You need:
except IntegrityError as e:
if 'unique constraint' in str(e.args).lower():
Example:
from django.db import IntegrityError
for column in csv.reader(io_string, delimiter=',', quotechar="|"):
try:
_, created = Brand.objects.update_or_create(
system_id=column[0],
name=column[1],
slug=column[2],
description=column[3],
image=column[4]
)
except IntegrityError as e:
if 'unique constraint' in str(e.args).lower():
continue
else:
raise e # Not the unique error we were expecting
- [Django]-What is the difference between {% load staticfiles %} and {% load static %}
- [Django]-What's the best way to migrate a Django DB from SQLite to MySQL?
- [Django]-How to implement FirebaseDB with a Django Web Application
2👍
Found in django.db.models.query.QuerySet._create_object_from_params
. And with some change:
from typing import Any, Dict, Type
from django.db import transaction, IntegrityError, Model
def get_or_create_obj(model: Type[Model], defaults: Dict[str, Any] = None, **kwargs: Any):
defaults = defaults or {}
try:
with transaction.atomic():
return model.objects.create(**kwargs, **defaults), True
except IntegrityError as e:
try:
return model.objects.using("default").get(**kwargs), False
except model.DoesNotExist:
pass
raise e
- [Django]-Coercing to Unicode: need string or buffer, NoneType found when rendering in django admin
- [Django]-Set Django IntegerField by choices=… name
- [Django]-Django runserver permanent
1👍
Unique constraint fail return :”(‘UNIQUE constraint failed: notepad_notepadform.title’)” which is basically a tuple,So we can use below code to catch it and do whatever required:
from django.db import IntegrityError
try:
if form.is_valid():
form.save()
except IntegrityError as e:
if 'UNIQUE constraint' in str(e.args):
#your code here
- [Django]-Remove pk field from django serialized objects
- [Django]-ForeignKey to abstract class (generic relations)
- [Django]-Django check if object in ManyToMany field
1👍
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")
- [Django]-Can I access constants in settings.py from templates in Django?
- [Django]-Django: using blocks in included templates
- [Django]-Django migrations RunPython not able to call model methods