mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 10:51:09 -05:00
Implement hybrid filtering strategy for parks and rides
- Added comprehensive documentation for hybrid filtering implementation, including architecture, API endpoints, performance characteristics, and usage examples. - Developed a hybrid pagination and client-side filtering recommendation, detailing server-side responsibilities and client-side logic. - Created a test script for hybrid filtering endpoints, covering various test cases including basic filtering, search functionality, pagination, and edge cases.
This commit is contained in:
@@ -538,6 +538,10 @@ class Ride(TrackedModel):
|
||||
max_digits=3, decimal_places=2, null=True, blank=True
|
||||
)
|
||||
|
||||
# Computed fields for hybrid filtering
|
||||
opening_year = models.IntegerField(null=True, blank=True, db_index=True)
|
||||
search_text = models.TextField(blank=True, db_index=True)
|
||||
|
||||
# Image settings - references to existing photos
|
||||
banner_image = models.ForeignKey(
|
||||
"RidePhoto",
|
||||
@@ -639,14 +643,14 @@ class Ride(TrackedModel):
|
||||
pass
|
||||
|
||||
# If park changed or this is a new ride, ensure slug uniqueness within the park
|
||||
park_changed = original_ride and original_ride.park_id != self.park_id
|
||||
park_changed = original_ride and original_ride.park.id != self.park.id
|
||||
if not self.pk or park_changed:
|
||||
self._ensure_unique_slug_in_park()
|
||||
|
||||
# Handle park area validation when park changes
|
||||
if park_changed and self.park_area:
|
||||
# Check if park_area belongs to the new park
|
||||
if self.park_area.park_id != self.park_id:
|
||||
if self.park_area.park.id != self.park.id:
|
||||
# Clear park_area if it doesn't belong to the new park
|
||||
self.park_area = None
|
||||
|
||||
@@ -658,8 +662,93 @@ class Ride(TrackedModel):
|
||||
self.url = f"{frontend_domain}/parks/{self.park.slug}/rides/{self.slug}/"
|
||||
self.park_url = f"{frontend_domain}/parks/{self.park.slug}/"
|
||||
|
||||
# Populate computed fields
|
||||
self._populate_computed_fields()
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def _populate_computed_fields(self) -> None:
|
||||
"""Populate computed fields for hybrid filtering."""
|
||||
# Extract opening year from opening_date
|
||||
if self.opening_date:
|
||||
self.opening_year = self.opening_date.year
|
||||
else:
|
||||
self.opening_year = None
|
||||
|
||||
# Build comprehensive search text
|
||||
search_parts = []
|
||||
|
||||
# Basic ride info
|
||||
if self.name:
|
||||
search_parts.append(self.name)
|
||||
if self.description:
|
||||
search_parts.append(self.description)
|
||||
|
||||
# Park info
|
||||
if self.park:
|
||||
search_parts.append(self.park.name)
|
||||
if hasattr(self.park, 'location') and self.park.location:
|
||||
if self.park.location.city:
|
||||
search_parts.append(self.park.location.city)
|
||||
if self.park.location.state:
|
||||
search_parts.append(self.park.location.state)
|
||||
if self.park.location.country:
|
||||
search_parts.append(self.park.location.country)
|
||||
|
||||
# Park area
|
||||
if self.park_area:
|
||||
search_parts.append(self.park_area.name)
|
||||
|
||||
# Category
|
||||
if self.category:
|
||||
category_display = dict(CATEGORY_CHOICES).get(self.category, '')
|
||||
if category_display:
|
||||
search_parts.append(category_display)
|
||||
|
||||
# Status
|
||||
if self.status:
|
||||
status_display = dict(self.STATUS_CHOICES).get(self.status, '')
|
||||
if status_display:
|
||||
search_parts.append(status_display)
|
||||
|
||||
# Companies
|
||||
if self.manufacturer:
|
||||
search_parts.append(self.manufacturer.name)
|
||||
if self.designer:
|
||||
search_parts.append(self.designer.name)
|
||||
|
||||
# Ride model
|
||||
if self.ride_model:
|
||||
search_parts.append(self.ride_model.name)
|
||||
if self.ride_model.manufacturer:
|
||||
search_parts.append(self.ride_model.manufacturer.name)
|
||||
|
||||
# Roller coaster stats if available
|
||||
try:
|
||||
if hasattr(self, 'coaster_stats') and self.coaster_stats:
|
||||
stats = self.coaster_stats
|
||||
if stats.track_type:
|
||||
search_parts.append(stats.track_type)
|
||||
if stats.track_material:
|
||||
material_display = dict(RollerCoasterStats.TRACK_MATERIAL_CHOICES).get(stats.track_material, '')
|
||||
if material_display:
|
||||
search_parts.append(material_display)
|
||||
if stats.roller_coaster_type:
|
||||
type_display = dict(RollerCoasterStats.COASTER_TYPE_CHOICES).get(stats.roller_coaster_type, '')
|
||||
if type_display:
|
||||
search_parts.append(type_display)
|
||||
if stats.launch_type:
|
||||
launch_display = dict(RollerCoasterStats.LAUNCH_CHOICES).get(stats.launch_type, '')
|
||||
if launch_display:
|
||||
search_parts.append(launch_display)
|
||||
if stats.train_style:
|
||||
search_parts.append(stats.train_style)
|
||||
except Exception:
|
||||
# Ignore if coaster_stats doesn't exist or has issues
|
||||
pass
|
||||
|
||||
self.search_text = ' '.join(filter(None, search_parts)).lower()
|
||||
|
||||
def _ensure_unique_slug_in_park(self) -> None:
|
||||
"""Ensure the ride's slug is unique within its park."""
|
||||
base_slug = slugify(self.name)
|
||||
@@ -685,7 +774,6 @@ class Ride(TrackedModel):
|
||||
Returns:
|
||||
dict: Summary of changes made
|
||||
"""
|
||||
from django.apps import apps
|
||||
|
||||
old_park = self.park
|
||||
old_url = self.url
|
||||
|
||||
Reference in New Issue
Block a user