Exceptions¶
django-api-orm provides a comprehensive exception hierarchy similar to Django’s ORM exceptions, making error handling familiar and predictable.
Exception Hierarchy¶
All exceptions inherit from APIException:
APIException
├── ValidationError (from Pydantic)
├── DoesNotExist
├── MultipleObjectsReturned
├── APIConnectionError
├── APITimeoutError
├── APIHTTPError
│ ├── APIClientError (4xx)
│ │ ├── BadRequestError (400)
│ │ ├── UnauthorizedError (401)
│ │ ├── ForbiddenError (403)
│ │ ├── NotFoundError (404)
│ │ └── TooManyRequestsError (429)
│ └── APIServerError (5xx)
│ ├── InternalServerError (500)
│ ├── BadGatewayError (502)
│ ├── ServiceUnavailableError (503)
│ └── GatewayTimeoutError (504)
└── ConfigurationError
Common Exceptions¶
DoesNotExist¶
Raised when a query returns no results when exactly one was expected:
from django_api_orm.exceptions import DoesNotExist
try:
user = User.objects.get(id=999)
except DoesNotExist:
print("User not found")
# Or catch model-specific exception
try:
user = User.objects.get(id=999)
except User.DoesNotExist:
print("User not found")
Common scenarios:
Model.objects.get()with no matching recordAPI returns 404 for a specific resource
MultipleObjectsReturned¶
Raised when a query returns multiple results when exactly one was expected:
from django_api_orm.exceptions import MultipleObjectsReturned
try:
# Multiple users with role="admin" exist
user = User.objects.get(role="admin")
except MultipleObjectsReturned:
print("Multiple users found, expected one")
# Or catch model-specific exception
try:
user = User.objects.get(role="admin")
except User.MultipleObjectsReturned:
print("Multiple users found")
Common scenarios:
Model.objects.get()with multiple matching recordsAPI returns a list when a single item was expected
ValidationError¶
Raised when data validation fails (from Pydantic):
from pydantic import ValidationError
try:
user = User.objects.create(
name="", # Empty name
email="invalid-email", # Invalid email
age=-5 # Invalid age
)
except ValidationError as e:
print(f"Validation failed: {e}")
for error in e.errors():
print(f" {error['loc']}: {error['msg']}")
Common scenarios:
Invalid field values during creation
Missing required fields
Type mismatches
Failed Pydantic validators
HTTP Exceptions¶
APIHTTPError¶
Base class for all HTTP-related errors:
from django_api_orm.exceptions import APIHTTPError
try:
user = User.objects.get(id=1)
except APIHTTPError as e:
print(f"HTTP error: {e.status_code} - {e.message}")
Client Errors (4xx)¶
BadRequestError (400): Invalid request data
from django_api_orm.exceptions import BadRequestError
try:
user = User.objects.create(name="Alice", email="alice@example.com")
except BadRequestError as e:
print(f"Bad request: {e.message}")
UnauthorizedError (401): Authentication required or failed
from django_api_orm.exceptions import UnauthorizedError
try:
user = User.objects.get(id=1)
except UnauthorizedError:
print("Authentication required or token expired")
ForbiddenError (403): Authenticated but not authorized
from django_api_orm.exceptions import ForbiddenError
try:
user.delete()
except ForbiddenError:
print("You don't have permission to delete this user")
NotFoundError (404): Resource not found
from django_api_orm.exceptions import NotFoundError
try:
user = User.objects.get(id=999)
except NotFoundError:
print("User not found")
TooManyRequestsError (429): Rate limit exceeded
from django_api_orm.exceptions import TooManyRequestsError
try:
users = User.objects.all()
except TooManyRequestsError as e:
print(f"Rate limit exceeded. Retry after: {e.retry_after}")
Server Errors (5xx)¶
InternalServerError (500): Server error
from django_api_orm.exceptions import InternalServerError
try:
user = User.objects.create(name="Alice", email="alice@example.com")
except InternalServerError:
print("Server encountered an error")
BadGatewayError (502): Bad gateway
from django_api_orm.exceptions import BadGatewayError
try:
users = User.objects.all()
except BadGatewayError:
print("Gateway error - upstream server issue")
ServiceUnavailableError (503): Service unavailable
from django_api_orm.exceptions import ServiceUnavailableError
try:
users = User.objects.all()
except ServiceUnavailableError as e:
print(f"Service unavailable. Retry after: {e.retry_after}")
GatewayTimeoutError (504): Gateway timeout
from django_api_orm.exceptions import GatewayTimeoutError
try:
users = User.objects.all()
except GatewayTimeoutError:
print("Gateway timeout - upstream server too slow")
Connection Exceptions¶
APIConnectionError¶
Raised when a connection to the API cannot be established:
from django_api_orm.exceptions import APIConnectionError
try:
user = User.objects.get(id=1)
except APIConnectionError as e:
print(f"Connection failed: {e.message}")
Common scenarios:
Network is down
Invalid base URL
DNS resolution failed
SSL/TLS errors
APITimeoutError¶
Raised when a request times out:
from django_api_orm.exceptions import APITimeoutError
try:
user = User.objects.get(id=1)
except APITimeoutError as e:
print(f"Request timed out after {e.timeout} seconds")
Common scenarios:
API is slow to respond
Large response taking too long
Network latency issues
Configuration Exceptions¶
ConfigurationError¶
Raised when there’s a configuration issue:
from django_api_orm.exceptions import ConfigurationError
try:
# Model not registered with client
user = User.objects.get(id=1)
except ConfigurationError as e:
print(f"Configuration error: {e.message}")
Common scenarios:
Model not registered with a client
Invalid endpoint configuration
Missing required configuration
Error Handling Patterns¶
Specific Exception Handling¶
Handle specific exceptions for fine-grained control:
from django_api_orm.exceptions import (
DoesNotExist,
UnauthorizedError,
NotFoundError,
APIServerError
)
try:
user = User.objects.get(id=user_id)
except DoesNotExist:
print("User not found")
except UnauthorizedError:
print("Please log in")
except NotFoundError:
print("API endpoint not found")
except APIServerError:
print("Server error, try again later")
Generic Exception Handling¶
Catch all API exceptions with APIException:
from django_api_orm.exceptions import APIException
try:
user = User.objects.get(id=user_id)
except APIException as e:
print(f"API error: {e}")
# Log error, notify admin, etc.
HTTP Status Code Handling¶
Handle errors by HTTP status code:
from django_api_orm.exceptions import APIHTTPError, APIClientError, APIServerError
try:
user = User.objects.get(id=user_id)
except APIClientError as e:
# 4xx errors - client issue
if e.status_code == 401:
print("Please log in")
elif e.status_code == 403:
print("Permission denied")
elif e.status_code == 404:
print("Not found")
else:
print(f"Client error: {e.status_code}")
except APIServerError as e:
# 5xx errors - server issue
print(f"Server error: {e.status_code}")
Retry Logic¶
Implement retry logic for transient errors:
import time
from django_api_orm.exceptions import (
APIServerError,
APITimeoutError,
TooManyRequestsError
)
max_retries = 3
retry_delay = 1 # seconds
for attempt in range(max_retries):
try:
user = User.objects.get(id=user_id)
break
except (APIServerError, APITimeoutError) as e:
if attempt < max_retries - 1:
time.sleep(retry_delay * (attempt + 1))
continue
raise
except TooManyRequestsError as e:
if attempt < max_retries - 1 and e.retry_after:
time.sleep(e.retry_after)
continue
raise
Fallback Values¶
Provide fallback values when resources don’t exist:
from django_api_orm.exceptions import DoesNotExist
try:
user = User.objects.get(email=email)
except DoesNotExist:
# Create a default user
user = User.objects.create(
email=email,
name="New User",
active=False
)
# Or use get_or_create
user, created = User.objects.get_or_create(
email=email,
defaults={"name": "New User", "active": False}
)
Logging Errors¶
Log errors for debugging and monitoring:
import logging
from django_api_orm.exceptions import APIException
logger = logging.getLogger(__name__)
try:
user = User.objects.get(id=user_id)
except APIException as e:
logger.error(
f"API error getting user {user_id}: {e}",
exc_info=True,
extra={
"user_id": user_id,
"exception_type": type(e).__name__,
}
)
raise
Async Exception Handling¶
Exception handling in async code works the same way:
from django_api_orm.exceptions import DoesNotExist, APIHTTPError
try:
user = await User.objects.get(id=user_id)
except DoesNotExist:
print("User not found")
except APIHTTPError as e:
print(f"HTTP error: {e.status_code}")
Best Practices¶
Be specific: Catch specific exceptions when possible
Log errors: Always log unexpected errors for debugging
Provide context: Include relevant information in error messages
Handle validation: Validate data before sending to API
Implement retries: Retry transient errors (5xx, timeouts)
Respect rate limits: Honor
retry_afterheadersUse fallbacks: Provide sensible defaults when appropriate
Don’t catch too broadly: Only catch exceptions you can handle
Exception Attributes¶
All exceptions have useful attributes:
from django_api_orm.exceptions import APIHTTPError
try:
user = User.objects.get(id=user_id)
except APIHTTPError as e:
print(f"Status code: {e.status_code}")
print(f"Message: {e.message}")
print(f"Response: {e.response}")
from django_api_orm.exceptions import TooManyRequestsError
try:
users = User.objects.all()
except TooManyRequestsError as e:
print(f"Retry after: {e.retry_after} seconds")
Complete Example¶
import logging
from django_api_orm.exceptions import (
DoesNotExist,
MultipleObjectsReturned,
ValidationError,
UnauthorizedError,
ForbiddenError,
NotFoundError,
TooManyRequestsError,
APIServerError,
APITimeoutError,
APIConnectionError,
)
logger = logging.getLogger(__name__)
def get_user_safely(user_id: int):
"""Get a user with comprehensive error handling."""
try:
return User.objects.get(id=user_id)
except DoesNotExist:
logger.warning(f"User {user_id} not found")
return None
except UnauthorizedError:
logger.error("Authentication failed")
raise # Re-raise to caller
except ForbiddenError:
logger.error(f"Access denied to user {user_id}")
raise
except NotFoundError:
logger.error("API endpoint not found")
return None
except TooManyRequestsError as e:
logger.warning(f"Rate limited. Retry after {e.retry_after}s")
raise
except APIServerError as e:
logger.error(f"Server error: {e.status_code} - {e.message}")
return None
except APITimeoutError:
logger.error(f"Request timed out for user {user_id}")
return None
except APIConnectionError as e:
logger.error(f"Connection failed: {e.message}")
return None
except Exception as e:
logger.exception(f"Unexpected error getting user {user_id}")
raise
Next Steps¶
See Models for model usage
Learn about QuerySets for querying
Explore Async Support for async exception handling