Skip to content

Release Notes

v2.23.0 Latest

Feb 23, 2026

📋 Release Notes

🏷️ [v2.23.0] - 2026-02-23


🔧 Improvements

🐍 NotFoundError Class-Name Key Uses snake_case

ninja_aio/exceptions.py

When NINJA_AIO_NOT_FOUND_ERROR_USE_VERBOSE_NAMES = False, the error key produced by NotFoundError is now automatically converted from CamelCase to snake_case (all lowercase), instead of using the raw Python class name.

Before (v2.22.0):

# settings.py
NINJA_AIO_NOT_FOUND_ERROR_USE_VERBOSE_NAMES = False

raise NotFoundError(BlogPost)
# {"BlogPost": "not found"}  ← raw class name

After (v2.23.0):

raise NotFoundError(BlogPost)
# {"blog_post": "not found"}  ← snake_case

raise NotFoundError(TestModelSerializer)
# {"test_model_serializer": "not found"}

This ensures the error key is consistent with standard JSON conventions and matches the format already used by the default verbose_name mode.

Implementation:

File Change
ninja_aio/exceptions.py model.__name__ converted via re.sub(r"(?<!^)(?=[A-Z])", "_", name).lower()

🧪 Tests

ExceptionsAndAPITestCase — updated

Test Verifies
test_not_found_error_class_name_mode use_verbose_name=False produces snake_case key

SubclassesTestCase — updated

Test Verifies
test_not_found_error_use_class_name use_verbose_name=False key matches snake_case(__name__)

🎯 Summary

Version 2.23.0 refines the use_verbose_name=False behaviour introduced in 2.22.0. The error key is now always snake_case, making it consistent with both the default verbose-name format and standard JSON naming conventions.

Key benefits:
- 🐍 Consistent casing — both modes now produce snake_case error keys
- ✅ Backwards-compatible — only affects the use_verbose_name=False opt-in mode