how to catch A 'UNIQUE constraint failed' 404 in django
Question:
How do I specifically catch a UNIQUE constraint failed 404 in the following code, I know I have to add something in the ( here? ) section
try:
q = AnswerModel(user=user, yes_question=question_model)
q.save()
except ( here? ):
return HttpResponseRedirect('/user/already_exists')
Answers:
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.
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)
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
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
For Python > 3.5
You need:
except IntegrityError as e:
if 'unique constraint' in e.args:
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 e.args:
continue
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
How do I specifically catch a UNIQUE constraint failed 404 in the following code, I know I have to add something in the ( here? ) section
try:
q = AnswerModel(user=user, yes_question=question_model)
q.save()
except ( here? ):
return HttpResponseRedirect('/user/already_exists')
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.
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)
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
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
For Python > 3.5
You need:
except IntegrityError as e:
if 'unique constraint' in e.args:
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 e.args:
continue
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