Skip to content

Raise a subclass of IntegrityError for duplicate key errors #83

@andrew-cybsafe

Description

@andrew-cybsafe

Code of Conduct

  • 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    In progress

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions