mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 05:11:09 -05:00
Refactor code structure and remove redundant changes
This commit is contained in:
233
backend/apps/api/v1/media/views.py
Normal file
233
backend/apps/api/v1/media/views.py
Normal file
@@ -0,0 +1,233 @@
|
||||
"""
|
||||
Media API views for ThrillWiki API v1.
|
||||
|
||||
This module provides API endpoints for media management including
|
||||
photo uploads, captions, and media operations.
|
||||
Consolidated from apps.media.views
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from typing import Any, Dict
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.http import Http404
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiParameter
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from rest_framework import status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.permissions import IsAuthenticated, AllowAny
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
from rest_framework.parsers import MultiPartParser, FormParser
|
||||
|
||||
# Import domain-specific models and services instead of generic Photo model
|
||||
from apps.parks.models import ParkPhoto
|
||||
from apps.rides.models import RidePhoto
|
||||
from apps.parks.services import ParkMediaService
|
||||
from apps.rides.services import RideMediaService
|
||||
from apps.core.services.media_service import MediaService
|
||||
from .serializers import (
|
||||
PhotoUploadInputSerializer,
|
||||
PhotoUploadOutputSerializer,
|
||||
PhotoDetailOutputSerializer,
|
||||
PhotoUpdateInputSerializer,
|
||||
PhotoListOutputSerializer,
|
||||
)
|
||||
from ..parks.serializers import ParkPhotoSerializer
|
||||
from ..rides.serializers import RidePhotoSerializer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@extend_schema_view(
|
||||
post=extend_schema(
|
||||
summary="Upload photo",
|
||||
description="Upload a photo and associate it with a content object (park, ride, etc.)",
|
||||
request=PhotoUploadInputSerializer,
|
||||
responses={
|
||||
201: PhotoUploadOutputSerializer,
|
||||
400: OpenApiTypes.OBJECT,
|
||||
403: OpenApiTypes.OBJECT,
|
||||
},
|
||||
tags=["Media"],
|
||||
),
|
||||
)
|
||||
class PhotoUploadAPIView(APIView):
|
||||
"""API endpoint for photo uploads."""
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
parser_classes = [MultiPartParser, FormParser]
|
||||
|
||||
def post(self, request: Request) -> Response:
|
||||
"""Upload a photo and associate it with a content object."""
|
||||
try:
|
||||
serializer = PhotoUploadInputSerializer(data=request.data)
|
||||
if not serializer.is_valid():
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
validated_data = serializer.validated_data
|
||||
|
||||
# Get content object
|
||||
try:
|
||||
content_type = ContentType.objects.get(
|
||||
app_label=validated_data["app_label"], model=validated_data["model"]
|
||||
)
|
||||
content_object = content_type.get_object_for_this_type(
|
||||
pk=validated_data["object_id"]
|
||||
)
|
||||
except ContentType.DoesNotExist:
|
||||
return Response(
|
||||
{
|
||||
"error": f"Invalid content type: {validated_data['app_label']}.{validated_data['model']}"
|
||||
},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
except content_type.model_class().DoesNotExist:
|
||||
return Response(
|
||||
{"error": "Content object not found"},
|
||||
status=status.HTTP_404_NOT_FOUND,
|
||||
)
|
||||
|
||||
# Determine which domain service to use based on content object
|
||||
if hasattr(content_object, '_meta') and content_object._meta.app_label == 'parks':
|
||||
# Check permissions for park photos
|
||||
if not request.user.has_perm("parks.add_parkphoto"):
|
||||
return Response(
|
||||
{"error": "You do not have permission to upload park photos"},
|
||||
status=status.HTTP_403_FORBIDDEN,
|
||||
)
|
||||
|
||||
# Create park photo using park media service
|
||||
photo = ParkMediaService.upload_photo(
|
||||
park=content_object,
|
||||
image_file=validated_data["photo"],
|
||||
user=request.user,
|
||||
caption=validated_data.get("caption", ""),
|
||||
alt_text=validated_data.get("alt_text", ""),
|
||||
is_primary=validated_data.get("is_primary", False),
|
||||
)
|
||||
elif hasattr(content_object, '_meta') and content_object._meta.app_label == 'rides':
|
||||
# Check permissions for ride photos
|
||||
if not request.user.has_perm("rides.add_ridephoto"):
|
||||
return Response(
|
||||
{"error": "You do not have permission to upload ride photos"},
|
||||
status=status.HTTP_403_FORBIDDEN,
|
||||
)
|
||||
|
||||
# Create ride photo using ride media service
|
||||
photo = RideMediaService.upload_photo(
|
||||
ride=content_object,
|
||||
image_file=validated_data["photo"],
|
||||
user=request.user,
|
||||
caption=validated_data.get("caption", ""),
|
||||
alt_text=validated_data.get("alt_text", ""),
|
||||
is_primary=validated_data.get("is_primary", False),
|
||||
photo_type=validated_data.get("photo_type", "general"),
|
||||
)
|
||||
else:
|
||||
return Response(
|
||||
{"error": f"Unsupported content type for media upload: {content_object._meta.label}"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
response_serializer = PhotoUploadOutputSerializer(
|
||||
{
|
||||
"id": photo.id,
|
||||
"url": photo.image.url,
|
||||
"caption": photo.caption,
|
||||
"alt_text": photo.alt_text,
|
||||
"is_primary": photo.is_primary,
|
||||
"message": "Photo uploaded successfully",
|
||||
}
|
||||
)
|
||||
|
||||
return Response(response_serializer.data, status=status.HTTP_201_CREATED)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in photo upload: {str(e)}", exc_info=True)
|
||||
return Response(
|
||||
{"error": f"An error occurred while uploading the photo: {str(e)}"},
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
)
|
||||
|
||||
|
||||
@extend_schema_view(
|
||||
list=extend_schema(
|
||||
summary="List photos",
|
||||
description="Retrieve a list of photos with optional filtering",
|
||||
parameters=[
|
||||
OpenApiParameter(
|
||||
name="content_type",
|
||||
type=OpenApiTypes.STR,
|
||||
location=OpenApiParameter.QUERY,
|
||||
description="Filter by content type (e.g., 'parks.park')",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="object_id",
|
||||
type=OpenApiTypes.INT,
|
||||
location=OpenApiParameter.QUERY,
|
||||
description="Filter by object ID",
|
||||
),
|
||||
],
|
||||
responses={200: PhotoListOutputSerializer(many=True)},
|
||||
tags=["Media"],
|
||||
),
|
||||
retrieve=extend_schema(
|
||||
summary="Get photo details",
|
||||
description="Retrieve detailed information about a specific photo",
|
||||
responses={
|
||||
200: PhotoDetailOutputSerializer,
|
||||
404: OpenApiTypes.OBJECT,
|
||||
},
|
||||
tags=["Media"],
|
||||
),
|
||||
update=extend_schema(
|
||||
summary="Update photo",
|
||||
description="Update photo information (caption, alt text, etc.)",
|
||||
request=PhotoUpdateInputSerializer,
|
||||
responses={
|
||||
200: PhotoDetailOutputSerializer,
|
||||
400: OpenApiTypes.OBJECT,
|
||||
403: OpenApiTypes.OBJECT,
|
||||
404: OpenApiTypes.OBJECT,
|
||||
},
|
||||
tags=["Media"],
|
||||
),
|
||||
destroy=extend_schema(
|
||||
summary="Delete photo",
|
||||
description="Delete a photo (only by owner or admin)",
|
||||
responses={
|
||||
204: None,
|
||||
403: OpenApiTypes.OBJECT,
|
||||
404: OpenApiTypes.OBJECT,
|
||||
},
|
||||
tags=["Media"],
|
||||
),
|
||||
set_primary=extend_schema(
|
||||
summary="Set photo as primary",
|
||||
description="Set this photo as the primary photo for its content object",
|
||||
responses={
|
||||
200: OpenApiTypes.OBJECT,
|
||||
403: OpenApiTypes.OBJECT,
|
||||
404: OpenApiTypes.OBJECT,
|
||||
},
|
||||
tags=["Media"],
|
||||
),
|
||||
)
|
||||
class PhotoViewSet(ModelViewSet):
|
||||
"""ViewSet for managing photos."""
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
lookup_field = "id"
|
||||
|
||||
def get_serializer_class(self):
|
||||
"""Return appropriate serializer based on action."""
|
||||
if self.action == "list":
|
||||
return PhotoListOutputSerializer
|
||||
elif self.action in ["update", "partial_update"]:
|
||||
return PhotoUpdateInputSerializer
|
||||
return PhotoDetailOutputSerializer
|
||||
Reference in New Issue
Block a user