feat(rides): populate slugs for existing RideModel records and ensure uniqueness

- Added migration 0011 to populate unique slugs for existing RideModel records based on manufacturer and model names.
- Implemented logic to ensure slug uniqueness during population.
- Added reverse migration to clear slugs if needed.

feat(rides): enforce unique slugs for RideModel

- Created migration 0012 to alter the slug field in RideModel to be unique.
- Updated the slug field to include help text and a maximum length of 255 characters.

docs: integrate Cloudflare Images into rides and parks models

- Updated RidePhoto and ParkPhoto models to use CloudflareImagesField for image storage.
- Enhanced API serializers for rides and parks to support Cloudflare Images, including new fields for image URLs and variants.
- Provided comprehensive OpenAPI schema metadata for new fields.
- Documented database migrations for the integration.
- Detailed configuration settings for Cloudflare Images.
- Updated API response formats to include Cloudflare Images URLs and variants.
- Added examples for uploading photos via API and outlined testing procedures.
This commit is contained in:
pacnpal
2025-08-28 15:12:39 -04:00
parent 715e284b3e
commit 67db0aa46e
34 changed files with 6002 additions and 894 deletions

View File

@@ -20,7 +20,7 @@ from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination
from rest_framework.exceptions import NotFound
from rest_framework.exceptions import NotFound, ValidationError
from drf_spectacular.utils import extend_schema, OpenApiParameter
from drf_spectacular.types import OpenApiTypes
@@ -30,6 +30,7 @@ from apps.api.v1.serializers.rides import (
RideDetailOutputSerializer,
RideCreateInputSerializer,
RideUpdateInputSerializer,
RideImageSettingsInputSerializer,
)
# Attempt to import model-level helpers; fall back gracefully if not present.
@@ -380,4 +381,46 @@ class RideSearchSuggestionsAPIView(APIView):
return Response(fallback)
# --- Ride image settings ---------------------------------------------------
@extend_schema(
summary="Set ride banner and card images",
description="Set banner_image and card_image for a ride from existing ride photos",
request=RideImageSettingsInputSerializer,
responses={
200: RideDetailOutputSerializer,
400: OpenApiTypes.OBJECT,
404: OpenApiTypes.OBJECT,
},
tags=["Rides"],
)
class RideImageSettingsAPIView(APIView):
permission_classes = [permissions.AllowAny]
def _get_ride_or_404(self, pk: int) -> Any:
if not MODELS_AVAILABLE:
raise NotFound("Ride models not available")
try:
return Ride.objects.get(pk=pk) # type: ignore
except Ride.DoesNotExist: # type: ignore
raise NotFound("Ride not found")
def patch(self, request: Request, pk: int) -> Response:
"""Set banner and card images for the ride."""
ride = self._get_ride_or_404(pk)
serializer = RideImageSettingsInputSerializer(data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
# Update the ride with the validated data
for field, value in serializer.validated_data.items():
setattr(ride, field, value)
ride.save()
# Return updated ride data
output_serializer = RideDetailOutputSerializer(
ride, context={"request": request})
return Response(output_serializer.data)
# --- Ride duplicate action --------------------------------------------------