mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 08:51:09 -05:00
Refactor code structure and remove redundant changes
This commit is contained in:
276
backend/api/v1/rides/views.py
Normal file
276
backend/api/v1/rides/views.py
Normal file
@@ -0,0 +1,276 @@
|
||||
"""
|
||||
Ride API views for ThrillWiki API v1.
|
||||
|
||||
This module contains consolidated ride photo viewset for the centralized API structure.
|
||||
"""
|
||||
import logging
|
||||
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from drf_spectacular.utils import extend_schema_view, extend_schema
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from rest_framework import status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from apps.rides.models import RidePhoto
|
||||
from apps.rides.services import RideMediaService
|
||||
|
||||
from .serializers import (
|
||||
RidePhotoOutputSerializer,
|
||||
RidePhotoCreateInputSerializer,
|
||||
RidePhotoUpdateInputSerializer,
|
||||
RidePhotoListOutputSerializer,
|
||||
RidePhotoApprovalInputSerializer,
|
||||
RidePhotoStatsOutputSerializer,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@extend_schema_view(
|
||||
list=extend_schema(
|
||||
summary="List ride photos",
|
||||
description="Retrieve a paginated list of ride photos with filtering capabilities.",
|
||||
responses={200: RidePhotoListOutputSerializer(many=True)},
|
||||
tags=["Ride Media"],
|
||||
),
|
||||
create=extend_schema(
|
||||
summary="Upload ride photo",
|
||||
description="Upload a new photo for a ride. Requires authentication.",
|
||||
request=RidePhotoCreateInputSerializer,
|
||||
responses={
|
||||
201: RidePhotoOutputSerializer,
|
||||
400: OpenApiTypes.OBJECT,
|
||||
401: OpenApiTypes.OBJECT,
|
||||
},
|
||||
tags=["Ride Media"],
|
||||
),
|
||||
retrieve=extend_schema(
|
||||
summary="Get ride photo details",
|
||||
description="Retrieve detailed information about a specific ride photo.",
|
||||
responses={
|
||||
200: RidePhotoOutputSerializer,
|
||||
404: OpenApiTypes.OBJECT,
|
||||
},
|
||||
tags=["Ride Media"],
|
||||
),
|
||||
update=extend_schema(
|
||||
summary="Update ride photo",
|
||||
description="Update ride photo information. Requires authentication and ownership or admin privileges.",
|
||||
request=RidePhotoUpdateInputSerializer,
|
||||
responses={
|
||||
200: RidePhotoOutputSerializer,
|
||||
400: OpenApiTypes.OBJECT,
|
||||
401: OpenApiTypes.OBJECT,
|
||||
403: OpenApiTypes.OBJECT,
|
||||
404: OpenApiTypes.OBJECT,
|
||||
},
|
||||
tags=["Ride Media"],
|
||||
),
|
||||
partial_update=extend_schema(
|
||||
summary="Partially update ride photo",
|
||||
description="Partially update ride photo information. Requires authentication and ownership or admin privileges.",
|
||||
request=RidePhotoUpdateInputSerializer,
|
||||
responses={
|
||||
200: RidePhotoOutputSerializer,
|
||||
400: OpenApiTypes.OBJECT,
|
||||
401: OpenApiTypes.OBJECT,
|
||||
403: OpenApiTypes.OBJECT,
|
||||
404: OpenApiTypes.OBJECT,
|
||||
},
|
||||
tags=["Ride Media"],
|
||||
),
|
||||
destroy=extend_schema(
|
||||
summary="Delete ride photo",
|
||||
description="Delete a ride photo. Requires authentication and ownership or admin privileges.",
|
||||
responses={
|
||||
204: None,
|
||||
401: OpenApiTypes.OBJECT,
|
||||
403: OpenApiTypes.OBJECT,
|
||||
404: OpenApiTypes.OBJECT,
|
||||
},
|
||||
tags=["Ride Media"],
|
||||
),
|
||||
)
|
||||
class RidePhotoViewSet(ModelViewSet):
|
||||
"""
|
||||
ViewSet for managing ride photos.
|
||||
|
||||
Provides CRUD operations for ride photos with proper permission checking.
|
||||
Uses RideMediaService for business logic operations.
|
||||
"""
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
lookup_field = "id"
|
||||
|
||||
def get_queryset(self):
|
||||
"""Get photos for the current ride with optimized queries."""
|
||||
return RidePhoto.objects.select_related(
|
||||
'ride',
|
||||
'ride__park',
|
||||
'uploaded_by'
|
||||
).filter(
|
||||
ride_id=self.kwargs.get('ride_pk')
|
||||
).order_by('-created_at')
|
||||
|
||||
def get_serializer_class(self):
|
||||
"""Return appropriate serializer based on action."""
|
||||
if self.action == 'list':
|
||||
return RidePhotoListOutputSerializer
|
||||
elif self.action == 'create':
|
||||
return RidePhotoCreateInputSerializer
|
||||
elif self.action in ['update', 'partial_update']:
|
||||
return RidePhotoUpdateInputSerializer
|
||||
else:
|
||||
return RidePhotoOutputSerializer
|
||||
|
||||
def perform_create(self, serializer):
|
||||
"""Create a new ride photo using RideMediaService."""
|
||||
ride_id = self.kwargs.get('ride_pk')
|
||||
if not ride_id:
|
||||
raise ValidationError("Ride ID is required")
|
||||
|
||||
try:
|
||||
# Use the service to create the photo with proper business logic
|
||||
photo = RideMediaService.create_photo(
|
||||
ride_id=ride_id,
|
||||
uploaded_by=self.request.user,
|
||||
**serializer.validated_data
|
||||
)
|
||||
|
||||
# Set the instance for the serializer response
|
||||
serializer.instance = photo
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating ride photo: {e}")
|
||||
raise ValidationError(f"Failed to create photo: {str(e)}")
|
||||
|
||||
def perform_update(self, serializer):
|
||||
"""Update ride photo with permission checking."""
|
||||
instance = self.get_object()
|
||||
|
||||
# Check permissions
|
||||
if not (self.request.user == instance.uploaded_by or self.request.user.is_staff):
|
||||
raise PermissionDenied("You can only edit your own photos or be an admin.")
|
||||
|
||||
# Handle primary photo logic using service
|
||||
if serializer.validated_data.get('is_primary', False):
|
||||
try:
|
||||
RideMediaService.set_primary_photo(
|
||||
ride_id=instance.ride_id,
|
||||
photo_id=instance.id
|
||||
)
|
||||
# Remove is_primary from validated_data since service handles it
|
||||
if 'is_primary' in serializer.validated_data:
|
||||
del serializer.validated_data['is_primary']
|
||||
except Exception as e:
|
||||
logger.error(f"Error setting primary photo: {e}")
|
||||
raise ValidationError(f"Failed to set primary photo: {str(e)}")
|
||||
|
||||
serializer.save()
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
"""Delete ride photo with permission checking."""
|
||||
# Check permissions
|
||||
if not (self.request.user == instance.uploaded_by or self.request.user.is_staff):
|
||||
raise PermissionDenied(
|
||||
"You can only delete your own photos or be an admin.")
|
||||
|
||||
try:
|
||||
RideMediaService.delete_photo(instance.id)
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting ride photo: {e}")
|
||||
raise ValidationError(f"Failed to delete photo: {str(e)}")
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def set_primary(self, request, **kwargs):
|
||||
"""Set this photo as the primary photo for the ride."""
|
||||
photo = self.get_object()
|
||||
|
||||
# Check permissions
|
||||
if not (request.user == photo.uploaded_by or request.user.is_staff):
|
||||
raise PermissionDenied(
|
||||
"You can only modify your own photos or be an admin.")
|
||||
|
||||
try:
|
||||
RideMediaService.set_primary_photo(
|
||||
ride_id=photo.ride_id,
|
||||
photo_id=photo.id
|
||||
)
|
||||
|
||||
# Refresh the photo instance
|
||||
photo.refresh_from_db()
|
||||
serializer = self.get_serializer(photo)
|
||||
|
||||
return Response(
|
||||
{
|
||||
'message': 'Photo set as primary successfully',
|
||||
'photo': serializer.data
|
||||
},
|
||||
status=status.HTTP_200_OK
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error setting primary photo: {e}")
|
||||
return Response(
|
||||
{'error': f'Failed to set primary photo: {str(e)}'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
@action(detail=False, methods=['post'], permission_classes=[IsAuthenticated])
|
||||
def bulk_approve(self, request, **kwargs):
|
||||
"""Bulk approve or reject multiple photos (admin only)."""
|
||||
if not request.user.is_staff:
|
||||
raise PermissionDenied("Only administrators can approve photos.")
|
||||
|
||||
serializer = RidePhotoApprovalInputSerializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
photo_ids = serializer.validated_data['photo_ids']
|
||||
approve = serializer.validated_data['approve']
|
||||
ride_id = self.kwargs.get('ride_pk')
|
||||
|
||||
try:
|
||||
# Filter photos to only those belonging to this ride
|
||||
photos = RidePhoto.objects.filter(
|
||||
id__in=photo_ids,
|
||||
ride_id=ride_id
|
||||
)
|
||||
|
||||
updated_count = photos.update(is_approved=approve)
|
||||
|
||||
return Response(
|
||||
{
|
||||
'message': f'Successfully {"approved" if approve else "rejected"} {updated_count} photos',
|
||||
'updated_count': updated_count
|
||||
},
|
||||
status=status.HTTP_200_OK
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in bulk photo approval: {e}")
|
||||
return Response(
|
||||
{'error': f'Failed to update photos: {str(e)}'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
@action(detail=False, methods=['get'])
|
||||
def stats(self, request, **kwargs):
|
||||
"""Get photo statistics for the ride."""
|
||||
ride_id = self.kwargs.get('ride_pk')
|
||||
|
||||
try:
|
||||
stats = RideMediaService.get_photo_stats(ride_id=ride_id)
|
||||
serializer = RidePhotoStatsOutputSerializer(stats)
|
||||
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting ride photo stats: {e}")
|
||||
return Response(
|
||||
{'error': f'Failed to get photo statistics: {str(e)}'},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
)
|
||||
Reference in New Issue
Block a user