feat: add public profiles list endpoint with search and pagination

- Add new /profiles/ endpoint for listing user profiles
- Support search by username/display name with ordering options
- Include pagination with configurable page size (max 100)
- Add comprehensive OpenAPI schema documentation
- Refactor passkey authentication state management in MFA flow
- Update URL routing and imports for new list_profiles view

This enables user discovery, leaderboards, and friend-finding features
with a publicly accessible, well-documented API endpoint.
This commit is contained in:
pacnpal
2026-01-10 13:00:02 -05:00
parent 22ff0d1c49
commit 692c0bbbbf
9 changed files with 424 additions and 45 deletions

View File

@@ -306,6 +306,12 @@ class HybridRideSerializer(serializers.ModelSerializer):
banner_image_url = serializers.SerializerMethodField()
card_image_url = serializers.SerializerMethodField()
# Metric unit conversions for frontend (duplicate of imperial fields)
coaster_height_meters = serializers.SerializerMethodField()
coaster_length_meters = serializers.SerializerMethodField()
coaster_speed_kmh = serializers.SerializerMethodField()
coaster_max_drop_meters = serializers.SerializerMethodField()
# Computed fields for filtering
opening_year = serializers.IntegerField(read_only=True)
search_text = serializers.CharField(read_only=True)
@@ -502,6 +508,47 @@ class HybridRideSerializer(serializers.ModelSerializer):
"""Check if ride has an announced closing date in the future."""
return obj.is_closing
# Metric conversions for frontend compatibility
@extend_schema_field(serializers.FloatField(allow_null=True))
def get_coaster_height_meters(self, obj):
"""Convert coaster height from feet to meters."""
try:
if hasattr(obj, "coaster_stats") and obj.coaster_stats and obj.coaster_stats.height_ft:
return round(float(obj.coaster_stats.height_ft) * 0.3048, 2)
return None
except (AttributeError, TypeError):
return None
@extend_schema_field(serializers.FloatField(allow_null=True))
def get_coaster_length_meters(self, obj):
"""Convert coaster length from feet to meters."""
try:
if hasattr(obj, "coaster_stats") and obj.coaster_stats and obj.coaster_stats.length_ft:
return round(float(obj.coaster_stats.length_ft) * 0.3048, 2)
return None
except (AttributeError, TypeError):
return None
@extend_schema_field(serializers.FloatField(allow_null=True))
def get_coaster_speed_kmh(self, obj):
"""Convert coaster speed from mph to km/h."""
try:
if hasattr(obj, "coaster_stats") and obj.coaster_stats and obj.coaster_stats.speed_mph:
return round(float(obj.coaster_stats.speed_mph) * 1.60934, 2)
return None
except (AttributeError, TypeError):
return None
@extend_schema_field(serializers.FloatField(allow_null=True))
def get_coaster_max_drop_meters(self, obj):
"""Convert coaster max drop from feet to meters."""
try:
if hasattr(obj, "coaster_stats") and obj.coaster_stats and obj.coaster_stats.max_drop_height_ft:
return round(float(obj.coaster_stats.max_drop_height_ft) * 0.3048, 2)
return None
except (AttributeError, TypeError):
return None
# Water ride stats fields
water_wetness_level = serializers.SerializerMethodField()
water_splash_height_ft = serializers.SerializerMethodField()

View File

@@ -12,6 +12,7 @@ from django.urls import include, path
from rest_framework.routers import DefaultRouter
from .photo_views import RidePhotoViewSet
from .ride_model_views import GlobalRideModelDetailAPIView, GlobalRideModelListAPIView
from .views import (
CompanySearchAPIView,
DesignerListAPIView,
@@ -40,6 +41,9 @@ urlpatterns = [
path("hybrid/filter-metadata/", RideFilterMetadataAPIView.as_view(), name="ride-hybrid-filter-metadata"),
# Filter options
path("filter-options/", FilterOptionsAPIView.as_view(), name="ride-filter-options"),
# Global ride model endpoints - matches frontend's /rides/models/ expectation
path("models/", GlobalRideModelListAPIView.as_view(), name="ride-model-global-list"),
path("models/<int:pk>/", GlobalRideModelDetailAPIView.as_view(), name="ride-model-global-detail"),
# Autocomplete / suggestion endpoints
path(
"search/companies/",