django / django/new-features

Raise a subclass of IntegrityError for duplicate key errors

Open
#83 8 comments 22 reactions 0 assignees View on GitHub
Dominant language
No language data
Stars
188
Forks
7
PR merge metrics
No merged PRs in 30d

Description

### Code of Conduct

- [x] I agree to follow Django's Code of Conduct

### Feature Description

When a new row is inserted that violates a unique key constraint, raise a `DuplicateKeyError` instead of `IntegrityError`. `DuplicateKeyError` would be a sub-class of `IntegrityError`.

The new exception could also be named: `DuplicateEntryError`, `DuplicateRowError`, `UniqueViolation`, `UniqueViolationError`, etc..

### Problem

I often need to understand whether an IntegrityError is caused by a duplicate entry exception versus other issues. For example, when processing incoming events from another system, it's typical to retry API calls when they fail and to provide a unique event_id to allow for de-duplication by the receiving system. In such a system, a duplicate key error can be ignored, whereas other errors might need to be handled elsewhere or logged so that a potential bug can be fixed.

### Request or proposal

request

### Additional Details

I currently workaround this on MySQL with the following helper method:
```
from django.db.utils import IntegrityError as DjangoIntegrityError
from django.db.utils import ProgrammingError
from MySQLdb import IntegrityError as MySQLIntegrityError
from MySQLdb.constants.ER import DUP_ENTRY

def is_mysql_duplicate_entry_error(exception):
"""Checks that a given exception was due to a MySQL duplicate entry error"""
if not isinstance(exception, DjangoIntegrityError):
return False

cause = exception.__cause__
if cause is None:
return False

if not isinstance(cause, MySQLIntegrityError):
raise ProgrammingError("Can only be used on MySQL exceptions")

code = cause.args[0] if cause.args else 0
return code == DUP_ENTRY
```

which is used like so:
```
try:
object.save()
except IntegrityError as e:
if is_mysql_duplicate_entry_error(e):
return
# Re-raise other errors to make them visible
raise e
```

It would be really nice to be able to write:
```
try:
object.save()
except DuplicateKeyError:
return # ignore duplicates
```

### Implementation Suggestions

- Subclass IntegrityError with an appropriately named exception
- Update the various DB adapters to raise the new exception when the relevant error code is returned from the DB
- Add tests
- Update docs & release notes

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with Django's IntegrityError definition and the database adapter error-handling paths; inspect how backend-specific duplicate-key codes are surfaced. Add tests for the relevant adapters, then update the documentation and release notes. Done means duplicate-key violations raise the chosen IntegrityError subclass while other integrity failures remain unchanged.

Written by the indexing model from the issue text.

Assessment

Tech stack
django, python
Domain
backend, databases
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.