""" Common mixins for API views following Django styleguide patterns. """ from typing import Dict, Any, Optional from rest_framework.request import Request from rest_framework.response import Response from rest_framework import status class ApiMixin: """ Base mixin for API views providing standardized response formatting. """ def create_response( self, *, data: Any = None, message: Optional[str] = None, status_code: int = status.HTTP_200_OK, pagination: Optional[Dict[str, Any]] = None, metadata: Optional[Dict[str, Any]] = None ) -> Response: """ Create standardized API response. Args: data: Response data message: Optional success message status_code: HTTP status code pagination: Pagination information metadata: Additional metadata Returns: Standardized Response object """ response_data = { 'status': 'success' if status_code < 400 else 'error', 'data': data, } if message: response_data['message'] = message if pagination: response_data['pagination'] = pagination if metadata: response_data['metadata'] = metadata return Response(response_data, status=status_code) def create_error_response( self, *, message: str, status_code: int = status.HTTP_400_BAD_REQUEST, error_code: Optional[str] = None, details: Optional[Dict[str, Any]] = None ) -> Response: """ Create standardized error response. Args: message: Error message status_code: HTTP status code error_code: Optional error code details: Additional error details Returns: Standardized error Response object """ error_data = { 'code': error_code or 'GENERIC_ERROR', 'message': message, } if details: error_data['details'] = details response_data = { 'status': 'error', 'error': error_data, 'data': None, } return Response(response_data, status=status_code) class CreateApiMixin(ApiMixin): """ Mixin for create API endpoints with standardized input/output handling. """ def create(self, request: Request, *args, **kwargs) -> Response: """Handle POST requests for creating resources.""" serializer = self.get_input_serializer(data=request.data) serializer.is_valid(raise_exception=True) # Create the object using the service layer obj = self.perform_create(**serializer.validated_data) # Serialize the output output_serializer = self.get_output_serializer(obj) return self.create_response( data=output_serializer.data, status_code=status.HTTP_201_CREATED, message="Resource created successfully" ) def perform_create(self, **validated_data): """ Override this method to implement object creation logic. Should use service layer methods. """ raise NotImplementedError("Subclasses must implement perform_create") def get_input_serializer(self, *args, **kwargs): """Get the input serializer for validation.""" return self.InputSerializer(*args, **kwargs) def get_output_serializer(self, *args, **kwargs): """Get the output serializer for response.""" return self.OutputSerializer(*args, **kwargs) class UpdateApiMixin(ApiMixin): """ Mixin for update API endpoints with standardized input/output handling. """ def update(self, request: Request, *args, **kwargs) -> Response: """Handle PUT/PATCH requests for updating resources.""" instance = self.get_object() serializer = self.get_input_serializer(data=request.data, partial=kwargs.get('partial', False)) serializer.is_valid(raise_exception=True) # Update the object using the service layer updated_obj = self.perform_update(instance, **serializer.validated_data) # Serialize the output output_serializer = self.get_output_serializer(updated_obj) return self.create_response( data=output_serializer.data, message="Resource updated successfully" ) def perform_update(self, instance, **validated_data): """ Override this method to implement object update logic. Should use service layer methods. """ raise NotImplementedError("Subclasses must implement perform_update") def get_input_serializer(self, *args, **kwargs): """Get the input serializer for validation.""" return self.InputSerializer(*args, **kwargs) def get_output_serializer(self, *args, **kwargs): """Get the output serializer for response.""" return self.OutputSerializer(*args, **kwargs) class ListApiMixin(ApiMixin): """ Mixin for list API endpoints with pagination and filtering. """ def list(self, request: Request, *args, **kwargs) -> Response: """Handle GET requests for listing resources.""" # Use selector to get filtered queryset queryset = self.get_queryset() # Apply pagination page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_output_serializer(page, many=True) return self.get_paginated_response(serializer.data) # No pagination serializer = self.get_output_serializer(queryset, many=True) return self.create_response(data=serializer.data) def get_queryset(self): """ Override this method to use selector patterns. Should call selector functions, not access model managers directly. """ raise NotImplementedError("Subclasses must implement get_queryset using selectors") def get_output_serializer(self, *args, **kwargs): """Get the output serializer for response.""" return self.OutputSerializer(*args, **kwargs) class RetrieveApiMixin(ApiMixin): """ Mixin for retrieve API endpoints. """ def retrieve(self, request: Request, *args, **kwargs) -> Response: """Handle GET requests for retrieving a single resource.""" instance = self.get_object() serializer = self.get_output_serializer(instance) return self.create_response(data=serializer.data) def get_object(self): """ Override this method to use selector patterns. Should call selector functions for optimized queries. """ raise NotImplementedError("Subclasses must implement get_object using selectors") def get_output_serializer(self, *args, **kwargs): """Get the output serializer for response.""" return self.OutputSerializer(*args, **kwargs) class DestroyApiMixin(ApiMixin): """ Mixin for delete API endpoints. """ def destroy(self, request: Request, *args, **kwargs) -> Response: """Handle DELETE requests for destroying resources.""" instance = self.get_object() # Delete using service layer self.perform_destroy(instance) return self.create_response( status_code=status.HTTP_204_NO_CONTENT, message="Resource deleted successfully" ) def perform_destroy(self, instance): """ Override this method to implement object deletion logic. Should use service layer methods. """ raise NotImplementedError("Subclasses must implement perform_destroy") def get_object(self): """ Override this method to use selector patterns. Should call selector functions for optimized queries. """ raise NotImplementedError("Subclasses must implement get_object using selectors")