mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 05:11:09 -05:00
Refactor API structure and add comprehensive user management features
- Restructure API v1 with improved serializers organization - Add user deletion requests and moderation queue system - Implement bulk moderation operations and permissions - Add user profile enhancements with display names and avatars - Expand ride and park API endpoints with better filtering - Add manufacturer API with detailed ride relationships - Improve authentication flows and error handling - Update frontend documentation and API specifications
This commit is contained in:
@@ -21,7 +21,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, ValidationError
|
||||
from rest_framework.exceptions import NotFound
|
||||
from drf_spectacular.utils import extend_schema, OpenApiParameter
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
|
||||
@@ -73,136 +73,202 @@ class RideListCreateAPIView(APIView):
|
||||
description="List rides with comprehensive filtering options including category, status, manufacturer, designer, ride model, and more.",
|
||||
parameters=[
|
||||
OpenApiParameter(
|
||||
name="page", location=OpenApiParameter.QUERY, type=OpenApiTypes.INT,
|
||||
description="Page number for pagination"
|
||||
name="page",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.INT,
|
||||
description="Page number for pagination",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="page_size", location=OpenApiParameter.QUERY, type=OpenApiTypes.INT,
|
||||
description="Number of results per page (max 1000)"
|
||||
name="page_size",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.INT,
|
||||
description="Number of results per page (max 1000)",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="search", location=OpenApiParameter.QUERY, type=OpenApiTypes.STR,
|
||||
description="Search in ride names and descriptions"
|
||||
name="search",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.STR,
|
||||
description="Search in ride names and descriptions",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="park_slug", location=OpenApiParameter.QUERY, type=OpenApiTypes.STR,
|
||||
description="Filter by park slug"
|
||||
name="park_slug",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.STR,
|
||||
description="Filter by park slug",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="park_id", location=OpenApiParameter.QUERY, type=OpenApiTypes.INT,
|
||||
description="Filter by park ID"
|
||||
name="park_id",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.INT,
|
||||
description="Filter by park ID",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="category", location=OpenApiParameter.QUERY, type=OpenApiTypes.STR,
|
||||
description="Filter by ride category (RC, DR, FR, WR, TR, OT). Multiple values supported: ?category=RC&category=DR"
|
||||
name="category",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.STR,
|
||||
description="Filter by ride category (RC, DR, FR, WR, TR, OT). Multiple values supported: ?category=RC&category=DR",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="status", location=OpenApiParameter.QUERY, type=OpenApiTypes.STR,
|
||||
description="Filter by ride status. Multiple values supported: ?status=OPERATING&status=CLOSED_TEMP"
|
||||
name="status",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.STR,
|
||||
description="Filter by ride status. Multiple values supported: ?status=OPERATING&status=CLOSED_TEMP",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="manufacturer_id", location=OpenApiParameter.QUERY, type=OpenApiTypes.INT,
|
||||
description="Filter by manufacturer company ID"
|
||||
name="manufacturer_id",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.INT,
|
||||
description="Filter by manufacturer company ID",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="manufacturer_slug", location=OpenApiParameter.QUERY, type=OpenApiTypes.STR,
|
||||
description="Filter by manufacturer company slug"
|
||||
name="manufacturer_slug",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.STR,
|
||||
description="Filter by manufacturer company slug",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="designer_id", location=OpenApiParameter.QUERY, type=OpenApiTypes.INT,
|
||||
description="Filter by designer company ID"
|
||||
name="designer_id",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.INT,
|
||||
description="Filter by designer company ID",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="designer_slug", location=OpenApiParameter.QUERY, type=OpenApiTypes.STR,
|
||||
description="Filter by designer company slug"
|
||||
name="designer_slug",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.STR,
|
||||
description="Filter by designer company slug",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="ride_model_id", location=OpenApiParameter.QUERY, type=OpenApiTypes.INT,
|
||||
description="Filter by specific ride model ID"
|
||||
name="ride_model_id",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.INT,
|
||||
description="Filter by specific ride model ID",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="ride_model_slug", location=OpenApiParameter.QUERY, type=OpenApiTypes.STR,
|
||||
description="Filter by ride model slug (requires manufacturer_slug)"
|
||||
name="ride_model_slug",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.STR,
|
||||
description="Filter by ride model slug (requires manufacturer_slug)",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="roller_coaster_type", location=OpenApiParameter.QUERY, type=OpenApiTypes.STR,
|
||||
description="Filter roller coasters by type (SITDOWN, INVERTED, FLYING, etc.)"
|
||||
name="roller_coaster_type",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.STR,
|
||||
description="Filter roller coasters by type (SITDOWN, INVERTED, FLYING, etc.)",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="track_material", location=OpenApiParameter.QUERY, type=OpenApiTypes.STR,
|
||||
description="Filter roller coasters by track material (STEEL, WOOD, HYBRID)"
|
||||
name="track_material",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.STR,
|
||||
description="Filter roller coasters by track material (STEEL, WOOD, HYBRID)",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="launch_type", location=OpenApiParameter.QUERY, type=OpenApiTypes.STR,
|
||||
description="Filter roller coasters by launch type (CHAIN, LSM, HYDRAULIC, etc.)"
|
||||
name="launch_type",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.STR,
|
||||
description="Filter roller coasters by launch type (CHAIN, LSM, HYDRAULIC, etc.)",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="min_rating", location=OpenApiParameter.QUERY, type=OpenApiTypes.NUMBER,
|
||||
description="Filter by minimum average rating (1-10)"
|
||||
name="min_rating",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.NUMBER,
|
||||
description="Filter by minimum average rating (1-10)",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="max_rating", location=OpenApiParameter.QUERY, type=OpenApiTypes.NUMBER,
|
||||
description="Filter by maximum average rating (1-10)"
|
||||
name="max_rating",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.NUMBER,
|
||||
description="Filter by maximum average rating (1-10)",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="min_height_requirement", location=OpenApiParameter.QUERY, type=OpenApiTypes.INT,
|
||||
description="Filter by minimum height requirement in inches"
|
||||
name="min_height_requirement",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.INT,
|
||||
description="Filter by minimum height requirement in inches",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="max_height_requirement", location=OpenApiParameter.QUERY, type=OpenApiTypes.INT,
|
||||
description="Filter by maximum height requirement in inches"
|
||||
name="max_height_requirement",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.INT,
|
||||
description="Filter by maximum height requirement in inches",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="min_capacity", location=OpenApiParameter.QUERY, type=OpenApiTypes.INT,
|
||||
description="Filter by minimum hourly capacity"
|
||||
name="min_capacity",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.INT,
|
||||
description="Filter by minimum hourly capacity",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="max_capacity", location=OpenApiParameter.QUERY, type=OpenApiTypes.INT,
|
||||
description="Filter by maximum hourly capacity"
|
||||
name="max_capacity",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.INT,
|
||||
description="Filter by maximum hourly capacity",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="min_height_ft", location=OpenApiParameter.QUERY, type=OpenApiTypes.NUMBER,
|
||||
description="Filter roller coasters by minimum height in feet"
|
||||
name="min_height_ft",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.NUMBER,
|
||||
description="Filter roller coasters by minimum height in feet",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="max_height_ft", location=OpenApiParameter.QUERY, type=OpenApiTypes.NUMBER,
|
||||
description="Filter roller coasters by maximum height in feet"
|
||||
name="max_height_ft",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.NUMBER,
|
||||
description="Filter roller coasters by maximum height in feet",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="min_speed_mph", location=OpenApiParameter.QUERY, type=OpenApiTypes.NUMBER,
|
||||
description="Filter roller coasters by minimum speed in mph"
|
||||
name="min_speed_mph",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.NUMBER,
|
||||
description="Filter roller coasters by minimum speed in mph",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="max_speed_mph", location=OpenApiParameter.QUERY, type=OpenApiTypes.NUMBER,
|
||||
description="Filter roller coasters by maximum speed in mph"
|
||||
name="max_speed_mph",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.NUMBER,
|
||||
description="Filter roller coasters by maximum speed in mph",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="min_inversions", location=OpenApiParameter.QUERY, type=OpenApiTypes.INT,
|
||||
description="Filter roller coasters by minimum number of inversions"
|
||||
name="min_inversions",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.INT,
|
||||
description="Filter roller coasters by minimum number of inversions",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="max_inversions", location=OpenApiParameter.QUERY, type=OpenApiTypes.INT,
|
||||
description="Filter roller coasters by maximum number of inversions"
|
||||
name="max_inversions",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.INT,
|
||||
description="Filter roller coasters by maximum number of inversions",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="has_inversions", location=OpenApiParameter.QUERY, type=OpenApiTypes.BOOL,
|
||||
description="Filter roller coasters that have inversions (true) or don't have inversions (false)"
|
||||
name="has_inversions",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.BOOL,
|
||||
description="Filter roller coasters that have inversions (true) or don't have inversions (false)",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="opening_year", location=OpenApiParameter.QUERY, type=OpenApiTypes.INT,
|
||||
description="Filter by opening year"
|
||||
name="opening_year",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.INT,
|
||||
description="Filter by opening year",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="min_opening_year", location=OpenApiParameter.QUERY, type=OpenApiTypes.INT,
|
||||
description="Filter by minimum opening year"
|
||||
name="min_opening_year",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.INT,
|
||||
description="Filter by minimum opening year",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="max_opening_year", location=OpenApiParameter.QUERY, type=OpenApiTypes.INT,
|
||||
description="Filter by maximum opening year"
|
||||
name="max_opening_year",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.INT,
|
||||
description="Filter by maximum opening year",
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="ordering", location=OpenApiParameter.QUERY, type=OpenApiTypes.STR,
|
||||
description="Order results by field. Options: name, -name, opening_date, -opening_date, average_rating, -average_rating, capacity_per_hour, -capacity_per_hour, created_at, -created_at, height_ft, -height_ft, speed_mph, -speed_mph"
|
||||
name="ordering",
|
||||
location=OpenApiParameter.QUERY,
|
||||
type=OpenApiTypes.STR,
|
||||
description="Order results by field. Options: name, -name, opening_date, -opening_date, average_rating, -average_rating, capacity_per_hour, -capacity_per_hour, created_at, -created_at, height_ft, -height_ft, speed_mph, -speed_mph",
|
||||
),
|
||||
],
|
||||
responses={200: RideListOutputSerializer(many=True)},
|
||||
@@ -220,17 +286,25 @@ class RideListCreateAPIView(APIView):
|
||||
)
|
||||
|
||||
# Start with base queryset with optimized joins
|
||||
qs = Ride.objects.all().select_related(
|
||||
"park", "manufacturer", "designer", "ride_model", "ride_model__manufacturer"
|
||||
).prefetch_related("coaster_stats") # type: ignore
|
||||
qs = (
|
||||
Ride.objects.all()
|
||||
.select_related(
|
||||
"park",
|
||||
"manufacturer",
|
||||
"designer",
|
||||
"ride_model",
|
||||
"ride_model__manufacturer",
|
||||
)
|
||||
.prefetch_related("coaster_stats")
|
||||
) # type: ignore
|
||||
|
||||
# Text search
|
||||
search = request.query_params.get("search")
|
||||
if search:
|
||||
qs = qs.filter(
|
||||
models.Q(name__icontains=search) |
|
||||
models.Q(description__icontains=search) |
|
||||
models.Q(park__name__icontains=search)
|
||||
models.Q(name__icontains=search)
|
||||
| models.Q(description__icontains=search)
|
||||
| models.Q(park__name__icontains=search)
|
||||
)
|
||||
|
||||
# Park filters
|
||||
@@ -292,7 +366,7 @@ class RideListCreateAPIView(APIView):
|
||||
if ride_model_slug and manufacturer_slug_for_model:
|
||||
qs = qs.filter(
|
||||
ride_model__slug=ride_model_slug,
|
||||
ride_model__manufacturer__slug=manufacturer_slug_for_model
|
||||
ride_model__manufacturer__slug=manufacturer_slug_for_model,
|
||||
)
|
||||
|
||||
# Rating filters
|
||||
@@ -422,24 +496,36 @@ class RideListCreateAPIView(APIView):
|
||||
|
||||
has_inversions = request.query_params.get("has_inversions")
|
||||
if has_inversions is not None:
|
||||
if has_inversions.lower() in ['true', '1', 'yes']:
|
||||
if has_inversions.lower() in ["true", "1", "yes"]:
|
||||
qs = qs.filter(coaster_stats__inversions__gt=0)
|
||||
elif has_inversions.lower() in ['false', '0', 'no']:
|
||||
elif has_inversions.lower() in ["false", "0", "no"]:
|
||||
qs = qs.filter(coaster_stats__inversions=0)
|
||||
|
||||
# Ordering
|
||||
ordering = request.query_params.get("ordering", "name")
|
||||
valid_orderings = [
|
||||
"name", "-name", "opening_date", "-opening_date",
|
||||
"average_rating", "-average_rating", "capacity_per_hour", "-capacity_per_hour",
|
||||
"created_at", "-created_at", "height_ft", "-height_ft", "speed_mph", "-speed_mph"
|
||||
"name",
|
||||
"-name",
|
||||
"opening_date",
|
||||
"-opening_date",
|
||||
"average_rating",
|
||||
"-average_rating",
|
||||
"capacity_per_hour",
|
||||
"-capacity_per_hour",
|
||||
"created_at",
|
||||
"-created_at",
|
||||
"height_ft",
|
||||
"-height_ft",
|
||||
"speed_mph",
|
||||
"-speed_mph",
|
||||
]
|
||||
|
||||
if ordering in valid_orderings:
|
||||
if ordering in ["height_ft", "-height_ft", "speed_mph", "-speed_mph"]:
|
||||
# For coaster stats ordering, we need to join and order by the stats
|
||||
ordering_field = ordering.replace("height_ft", "coaster_stats__height_ft").replace(
|
||||
"speed_mph", "coaster_stats__speed_mph")
|
||||
ordering_field = ordering.replace(
|
||||
"height_ft", "coaster_stats__height_ft"
|
||||
).replace("speed_mph", "coaster_stats__speed_mph")
|
||||
qs = qs.order_by(ordering_field)
|
||||
else:
|
||||
qs = qs.order_by(ordering)
|
||||
@@ -592,16 +678,24 @@ class FilterOptionsAPIView(APIView):
|
||||
"ordering_options": [
|
||||
{"value": "name", "label": "Name (A-Z)"},
|
||||
{"value": "-name", "label": "Name (Z-A)"},
|
||||
{"value": "opening_date",
|
||||
"label": "Opening Date (Oldest First)"},
|
||||
{"value": "-opening_date",
|
||||
"label": "Opening Date (Newest First)"},
|
||||
{
|
||||
"value": "opening_date",
|
||||
"label": "Opening Date (Oldest First)",
|
||||
},
|
||||
{
|
||||
"value": "-opening_date",
|
||||
"label": "Opening Date (Newest First)",
|
||||
},
|
||||
{"value": "average_rating", "label": "Rating (Lowest First)"},
|
||||
{"value": "-average_rating", "label": "Rating (Highest First)"},
|
||||
{"value": "capacity_per_hour",
|
||||
"label": "Capacity (Lowest First)"},
|
||||
{"value": "-capacity_per_hour",
|
||||
"label": "Capacity (Highest First)"},
|
||||
{
|
||||
"value": "capacity_per_hour",
|
||||
"label": "Capacity (Lowest First)",
|
||||
},
|
||||
{
|
||||
"value": "-capacity_per_hour",
|
||||
"label": "Capacity (Highest First)",
|
||||
},
|
||||
{"value": "height_ft", "label": "Height (Shortest First)"},
|
||||
{"value": "-height_ft", "label": "Height (Tallest First)"},
|
||||
{"value": "speed_mph", "label": "Speed (Slowest First)"},
|
||||
@@ -611,16 +705,39 @@ class FilterOptionsAPIView(APIView):
|
||||
],
|
||||
"filter_ranges": {
|
||||
"rating": {"min": 1, "max": 10, "step": 0.1},
|
||||
"height_requirement": {"min": 30, "max": 90, "step": 1, "unit": "inches"},
|
||||
"capacity": {"min": 0, "max": 5000, "step": 50, "unit": "riders/hour"},
|
||||
"height_requirement": {
|
||||
"min": 30,
|
||||
"max": 90,
|
||||
"step": 1,
|
||||
"unit": "inches",
|
||||
},
|
||||
"capacity": {
|
||||
"min": 0,
|
||||
"max": 5000,
|
||||
"step": 50,
|
||||
"unit": "riders/hour",
|
||||
},
|
||||
"height_ft": {"min": 0, "max": 500, "step": 5, "unit": "feet"},
|
||||
"speed_mph": {"min": 0, "max": 150, "step": 5, "unit": "mph"},
|
||||
"inversions": {"min": 0, "max": 20, "step": 1, "unit": "inversions"},
|
||||
"opening_year": {"min": 1800, "max": 2030, "step": 1, "unit": "year"},
|
||||
"inversions": {
|
||||
"min": 0,
|
||||
"max": 20,
|
||||
"step": 1,
|
||||
"unit": "inversions",
|
||||
},
|
||||
"opening_year": {
|
||||
"min": 1800,
|
||||
"max": 2030,
|
||||
"step": 1,
|
||||
"unit": "year",
|
||||
},
|
||||
},
|
||||
"boolean_filters": [
|
||||
{"key": "has_inversions", "label": "Has Inversions",
|
||||
"description": "Filter roller coasters with or without inversions"},
|
||||
{
|
||||
"key": "has_inversions",
|
||||
"label": "Has Inversions",
|
||||
"description": "Filter roller coasters with or without inversions",
|
||||
},
|
||||
],
|
||||
}
|
||||
return Response(data)
|
||||
@@ -682,8 +799,10 @@ class FilterOptionsAPIView(APIView):
|
||||
{"value": "average_rating", "label": "Rating (Lowest First)"},
|
||||
{"value": "-average_rating", "label": "Rating (Highest First)"},
|
||||
{"value": "capacity_per_hour", "label": "Capacity (Lowest First)"},
|
||||
{"value": "-capacity_per_hour",
|
||||
"label": "Capacity (Highest First)"},
|
||||
{
|
||||
"value": "-capacity_per_hour",
|
||||
"label": "Capacity (Highest First)",
|
||||
},
|
||||
{"value": "height_ft", "label": "Height (Shortest First)"},
|
||||
{"value": "-height_ft", "label": "Height (Tallest First)"},
|
||||
{"value": "speed_mph", "label": "Speed (Slowest First)"},
|
||||
@@ -693,16 +812,39 @@ class FilterOptionsAPIView(APIView):
|
||||
],
|
||||
"filter_ranges": {
|
||||
"rating": {"min": 1, "max": 10, "step": 0.1},
|
||||
"height_requirement": {"min": 30, "max": 90, "step": 1, "unit": "inches"},
|
||||
"capacity": {"min": 0, "max": 5000, "step": 50, "unit": "riders/hour"},
|
||||
"height_requirement": {
|
||||
"min": 30,
|
||||
"max": 90,
|
||||
"step": 1,
|
||||
"unit": "inches",
|
||||
},
|
||||
"capacity": {
|
||||
"min": 0,
|
||||
"max": 5000,
|
||||
"step": 50,
|
||||
"unit": "riders/hour",
|
||||
},
|
||||
"height_ft": {"min": 0, "max": 500, "step": 5, "unit": "feet"},
|
||||
"speed_mph": {"min": 0, "max": 150, "step": 5, "unit": "mph"},
|
||||
"inversions": {"min": 0, "max": 20, "step": 1, "unit": "inversions"},
|
||||
"opening_year": {"min": 1800, "max": 2030, "step": 1, "unit": "year"},
|
||||
"inversions": {
|
||||
"min": 0,
|
||||
"max": 20,
|
||||
"step": 1,
|
||||
"unit": "inversions",
|
||||
},
|
||||
"opening_year": {
|
||||
"min": 1800,
|
||||
"max": 2030,
|
||||
"step": 1,
|
||||
"unit": "year",
|
||||
},
|
||||
},
|
||||
"boolean_filters": [
|
||||
{"key": "has_inversions", "label": "Has Inversions",
|
||||
"description": "Filter roller coasters with or without inversions"},
|
||||
{
|
||||
"key": "has_inversions",
|
||||
"label": "Has Inversions",
|
||||
"description": "Filter roller coasters with or without inversions",
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
@@ -853,7 +995,8 @@ class RideImageSettingsAPIView(APIView):
|
||||
|
||||
# Return updated ride data
|
||||
output_serializer = RideDetailOutputSerializer(
|
||||
ride, context={"request": request})
|
||||
ride, context={"request": request}
|
||||
)
|
||||
return Response(output_serializer.data)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user