Exceptions¶
Django Ninja AIO CRUD provides a structured exception hierarchy for consistent error handling across all API endpoints.
Overview¶
All framework exceptions inherit from BaseException and carry a serializable error payload and an HTTP status code. Exception handlers registered on the NinjaAIO instance automatically convert them into JSON responses.
from ninja_aio.exceptions import (
BaseException,
SerializeError,
AuthError,
NotFoundError,
PydanticValidationError,
)
Exception Classes¶
BaseException¶
Base class for all framework exceptions.
Constructor:
BaseException(
error: str | dict = None,
status_code: int | None = None,
details: str | None = None,
)
| Parameter | Type | Description |
|---|---|---|
error |
str \| dict |
Error payload. Strings are wrapped under the "error" key; dicts are used directly. |
status_code |
int \| None |
HTTP status code. Defaults to 400. |
details |
str \| None |
Optional detail message merged into the error dict under "details". |
Example:
# String error
exc = BaseException("Something went wrong", 400)
print(exc.error) # {"error": "Something went wrong"}
# Dict error with details
exc = BaseException({"field": "invalid"}, details="must be positive")
print(exc.error) # {"field": "invalid", "details": "must be positive"}
SerializeError¶
Raised when serialization of request or response payloads fails. Inherits directly from BaseException with the same interface.
AuthError¶
Raised when authentication or authorization fails. Inherits from BaseException.
NotFoundError¶
Raised when a requested model instance cannot be found. Always returns HTTP 404.
class NotFoundError(BaseException):
status_code = 404
error = "not found"
use_verbose_name = getattr(
settings, "NINJA_AIO_NOT_FOUND_ERROR_USE_VERBOSE_NAMES", True
)
Constructor:
Error key format:
By default the error key is the model's verbose_name with spaces replaced by underscores:
# Model with verbose_name = "blog post"
raise NotFoundError(BlogPost)
# {"blog_post": "not found"}
When NINJA_AIO_NOT_FOUND_ERROR_USE_VERBOSE_NAMES = False the error key is derived from the model class name converted to snake_case:
# settings.py
NINJA_AIO_NOT_FOUND_ERROR_USE_VERBOSE_NAMES = False
raise NotFoundError(BlogPost)
# {"blog_post": "not found"}
raise NotFoundError(TestModelSerializer)
# {"test_model_serializer": "not found"}
NINJA_AIO_NOT_FOUND_ERROR_USE_VERBOSE_NAMES¶
| Value | Error key source | Example |
|---|---|---|
True (default) |
model._meta.verbose_name with spaces → _ |
{"blog_post": "not found"} |
False |
model.__name__ converted to snake_case |
{"test_model_serializer": "not found"} |
Configure in settings.py:
# Use snake_case model class name in not-found errors
NINJA_AIO_NOT_FOUND_ERROR_USE_VERBOSE_NAMES = False
PydanticValidationError¶
Wraps a Pydantic ValidationError into a normalized 400 response.
Response format:
Exception Handlers¶
Exception handlers are automatically registered when using NinjaAIO. You can also register them manually:
from ninja_aio.exceptions import set_api_exception_handlers
api = NinjaAIO()
set_api_exception_handlers(api)
| Exception type | Handler | Response code |
|---|---|---|
BaseException |
_default_error |
From exc.status_code |
JoseError |
_jose_error |
401 |
ValidationError |
_pydantic_validation_error |
400 |
Complete Example¶
from ninja_aio.exceptions import NotFoundError, SerializeError
async def get_article(request, pk: int):
try:
article = await Article.objects.aget(pk=pk)
except Article.DoesNotExist:
raise NotFoundError(Article) # {"article": "not found"}, 404
return article
async def create_article(request, data):
try:
payload, customs = await util.parse_input_data(request, data)
except SerializeError as e:
# Already handled by NinjaAIO exception handlers
raise
return await Article.objects.acreate(**payload)
See Also¶
-
ModelUtil
-
Authentication