Files
thrillwiki_django_no_react/backend/docs/error_handling.md
pacnpal 2e35f8c5d9 feat: Refactor rides app with unique constraints, mixins, and enhanced documentation
- Added migration to convert unique_together constraints to UniqueConstraint for RideModel.
- Introduced RideFormMixin for handling entity suggestions in ride forms.
- Created comprehensive code standards documentation outlining formatting, docstring requirements, complexity guidelines, and testing requirements.
- Established error handling guidelines with a structured exception hierarchy and best practices for API and view error handling.
- Documented view pattern guidelines, emphasizing the use of CBVs, FBVs, and ViewSets with examples.
- Implemented a benchmarking script for query performance analysis and optimization.
- Developed security documentation detailing measures, configurations, and a security checklist.
- Compiled a database optimization guide covering indexing strategies, query optimization patterns, and computed fields.
2025-12-22 11:17:31 -05:00

5.3 KiB

Error Handling Guidelines

This document provides guidelines for handling errors consistently across the ThrillWiki backend.

Exception Hierarchy

ThrillWiki uses a structured exception hierarchy defined in apps/core/exceptions.py:

ThrillWikiException (base)
├── ValidationException (400)
├── NotFoundError (404)
├── PermissionDeniedError (403)
├── BusinessLogicError (400)
├── ServiceError (500)
├── ExternalServiceError (502)
├── CacheError (500)
│
├── Domain-specific exceptions:
│   ├── ParkError
│   │   ├── ParkNotFoundError
│   │   └── ParkOperationError
│   ├── RideError
│   │   ├── RideNotFoundError
│   │   └── RideOperationError
│   ├── LocationError
│   │   ├── InvalidCoordinatesError
│   │   └── GeolocationError
│   ├── ReviewError
│   │   ├── ReviewModerationError
│   │   └── DuplicateReviewError
│   └── AccountError
│       ├── InsufficientPermissionsError
│       └── EmailError

Using ErrorHandler

The ErrorHandler class in apps/core/utils/error_handling.py provides standardized error handling.

Template Views

from apps.core.utils.error_handling import ErrorHandler
from apps.core.exceptions import ServiceError

def my_view(request):
    try:
        result = SomeService.do_operation(...)
    except ServiceError as e:
        ErrorHandler.handle_view_error(
            request,
            e,
            user_message="The operation failed. Please try again.",
            log_message=f"Service operation failed for user {request.user.id}"
        )
        return redirect("some-fallback")
    except ValidationError as e:
        ErrorHandler.handle_view_error(
            request,
            e,
            user_message="Invalid data provided",
            level="warning"
        )
        return redirect("form-view")

API Views

from apps.core.utils.error_handling import ErrorHandler
from apps.core.exceptions import ServiceError
from rest_framework import status

class MyAPIView(APIView):
    def post(self, request):
        try:
            result = SomeService.do_operation(...)
            return ErrorHandler.api_success_response(
                data=result,
                message="Operation completed successfully"
            )
        except ServiceError as e:
            return ErrorHandler.handle_api_error(
                e,
                user_message="Failed to complete operation",
                status_code=status.HTTP_400_BAD_REQUEST
            )

Best Practices

1. Always Catch Specific Exceptions

# Good
try:
    park = ParkService.create_park(...)
except ParkOperationError as e:
    # Handle park-specific error
    pass
except ValidationException as e:
    # Handle validation error
    pass

# Bad
try:
    park = ParkService.create_park(...)
except Exception as e:
    # Too broad - loses error context
    pass

2. Log with Appropriate Context

# Good
logger.error(
    f"Park creation failed for user {user.id}: {error}",
    exc_info=True,
    extra={"user_id": user.id, "park_name": name}
)

# Bad
logger.error(f"Error: {error}")

3. Provide Clear User Messages

# Good - User-friendly and actionable
ErrorHandler.handle_view_error(
    request,
    error,
    user_message="Unable to save your changes. Please check your input and try again."
)

# Bad - Technical details exposed to user
ErrorHandler.handle_view_error(
    request,
    error,
    user_message=f"IntegrityError: UNIQUE constraint failed: parks_park.slug"
)

4. Use Appropriate HTTP Status Codes

Error Type Status Code When to Use
ValidationException 400 Invalid user input
NotFoundError 404 Resource doesn't exist
PermissionDeniedError 403 User lacks permission
BusinessLogicError 400 Business rule violation
ServiceError 500 Internal service failure
ExternalServiceError 502 Third-party service failure

5. Never Use Bare except: Clauses

# Never do this
try:
    something()
except:
    pass

# Always specify exception type
try:
    something()
except SpecificException:
    handle_error()

Error Response Format

API Error Response

{
    "error": "User-friendly error message",
    "detail": "Technical error details",
    "error_code": "SPECIFIC_ERROR_CODE",
    "details": {
        "field": "Additional context"
    }
}

API Success Response

{
    "status": "success",
    "message": "Operation completed successfully",
    "data": {
        // Response data
    }
}

Creating Custom Exceptions

When creating domain-specific exceptions:

from apps.core.exceptions import BusinessLogicError

class MyDomainError(BusinessLogicError):
    """Raised when my domain operation fails."""

    default_message = "My domain operation failed"
    error_code = "MY_DOMAIN_ERROR"
    status_code = 400

    def __init__(self, context_value: str = None, **kwargs):
        if context_value:
            kwargs["details"] = {"context": context_value}
            kwargs["message"] = f"Operation failed for: {context_value}"
        super().__init__(**kwargs)