Based on the git diff provided, here's a concise and descriptive commit message:

feat: add passkey authentication and enhance user preferences

- Add passkey login security event type with fingerprint icon
- Include request and site context in email confirmation for backend
- Add user_id exact match filter to prevent incorrect user lookups
- Enable PATCH method for updating user preferences via API
- Add moderation_preferences support to user settings
- Optimize ticket queries with select_related and prefetch_related

This commit introduces passkey authentication tracking, improves user
profile filtering accuracy, and extends the preferences API to support
updates. Query optimizations reduce database hits for ticket listings.
This commit is contained in:
pacnpal
2026-01-12 19:13:05 -05:00
parent 2b66814d82
commit d631f3183c
56 changed files with 5860 additions and 264 deletions

View File

@@ -17,12 +17,14 @@ from .rankings import RankingSnapshot, RidePairComparison, RideRanking
from .reviews import RideReview
from .rides import Ride, RideModel, RollerCoasterStats
from .stats import DarkRideStats, FlatRideStats, KiddieRideStats, TransportationStats, WaterRideStats
from .sub_types import RideSubType
__all__ = [
# Primary models
"Ride",
"RideModel",
"RideNameHistory",
"RideSubType",
"RollerCoasterStats",
"WaterRideStats",
"DarkRideStats",

View File

@@ -0,0 +1,67 @@
"""
RideSubType model for categorizing rides by sub-type within each category.
This model replaces the legacy Supabase ride_sub_types table,
providing a lookup table for ride sub-types (e.g., 'Flying Coaster',
'Inverted Coaster' for roller coasters).
"""
from django.conf import settings
from django.db import models
from apps.core.choices import RichChoiceField
from apps.core.models import TrackedModel
class RideSubType(TrackedModel):
"""
Lookup table for ride sub-types categorized by ride category.
Examples:
- Roller Coaster: Flying Coaster, Inverted Coaster, Dive Coaster
- Water Ride: Log Flume, River Rapids, Splash Battle
- Dark Ride: Trackless, Omnimover, Boat Ride
"""
name = models.CharField(
max_length=100,
help_text="Name of the ride sub-type (e.g., 'Flying Coaster')",
)
category = RichChoiceField(
choice_group="categories",
domain="rides",
max_length=2,
help_text="Ride category this sub-type belongs to",
)
description = models.TextField(
blank=True,
help_text="Description of this ride sub-type",
)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="created_ride_sub_types",
help_text="User who created this sub-type",
)
class Meta(TrackedModel.Meta):
verbose_name = "Ride Sub-Type"
verbose_name_plural = "Ride Sub-Types"
ordering = ["category", "name"]
unique_together = [["category", "name"]]
def __str__(self) -> str:
return f"{self.name} ({self.get_category_display()})"
def get_category_display(self) -> str:
"""Get human-readable category label."""
from apps.core.choices.registry import get_choice_group
group = get_choice_group("categories", domain="rides")
if group:
for choice in group.choices:
if choice.value == self.category:
return choice.label
return self.category