mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-23 01:31:09 -05:00
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.
This commit is contained in:
219
backend/docs/error_handling.md
Normal file
219
backend/docs/error_handling.md
Normal file
@@ -0,0 +1,219 @@
|
||||
# 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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# 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
|
||||
|
||||
```python
|
||||
# Never do this
|
||||
try:
|
||||
something()
|
||||
except:
|
||||
pass
|
||||
|
||||
# Always specify exception type
|
||||
try:
|
||||
something()
|
||||
except SpecificException:
|
||||
handle_error()
|
||||
```
|
||||
|
||||
## Error Response Format
|
||||
|
||||
### API Error Response
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "User-friendly error message",
|
||||
"detail": "Technical error details",
|
||||
"error_code": "SPECIFIC_ERROR_CODE",
|
||||
"details": {
|
||||
"field": "Additional context"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### API Success Response
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"message": "Operation completed successfully",
|
||||
"data": {
|
||||
// Response data
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Creating Custom Exceptions
|
||||
|
||||
When creating domain-specific exceptions:
|
||||
|
||||
```python
|
||||
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)
|
||||
```
|
||||
Reference in New Issue
Block a user