Add views for ride management including detail, creation, and listing; implement search functionality for manufacturers, designers, and ride models

This commit is contained in:
pacnpal
2025-02-11 10:41:40 -05:00
parent 6034227796
commit 2e0d32819a

View File

@@ -1,17 +1,144 @@
# Keep all imports and previous classes up to RideCreateView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import get_object_or_404
from typing import Any, Dict, Optional, Tuple, Union, cast, Type
from django.views.generic import DetailView, ListView, CreateView, UpdateView
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views.generic import UpdateView
from django.db.models import Q, Model
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.decorators import login_required
from django.contrib.contenttypes.models import ContentType
from django.contrib import messages
from django.http import HttpRequest, HttpResponse, Http404
from django.db.models import Count
from .models import (
Ride, RollerCoasterStats, RideModel, RideEvent,
CATEGORY_CHOICES
)
from .forms import RideForm
from parks.models import Park
from core.views import SlugRedirectMixin
from moderation.mixins import EditSubmissionMixin, PhotoSubmissionMixin, HistoryMixin
from moderation.models import EditSubmission
from companies.models import Manufacturer
from designers.models import Designer
from moderation.mixins import EditSubmissionMixin
from apps.parks.mixins.base import ParkContextRequired # type: ignore
from moderation.models import EditSubmission
from parks.models import Park
from rides.forms import RideForm # type: ignore
from .models import Ride, RideModel
class ParkContextRequired:
"""Mixin to require park context for views"""
def dispatch(self, request, *args, **kwargs):
if 'park_slug' not in self.kwargs:
raise Http404("Park context is required")
return super().dispatch(request, *args, **kwargs)
def show_coaster_fields(request: HttpRequest) -> HttpResponse:
"""Show roller coaster specific fields based on category selection"""
category = request.GET.get('category')
if category != 'RC': # Only show for roller coasters
return HttpResponse('')
return render(request, "rides/partials/coaster_fields.html")
class RideDetailView(HistoryMixin, DetailView):
"""View for displaying ride details"""
model = Ride
template_name = 'rides/ride_detail.html'
slug_url_kwarg = 'ride_slug'
def get_queryset(self):
"""Get ride for the specific park if park_slug is provided"""
queryset = Ride.objects.all().select_related(
'park',
'ride_model',
'ride_model__manufacturer'
).prefetch_related('photos')
if 'park_slug' in self.kwargs:
queryset = queryset.filter(park__slug=self.kwargs['park_slug'])
return queryset
def get_context_data(self, **kwargs):
"""Add context data"""
context = super().get_context_data(**kwargs)
if 'park_slug' in self.kwargs:
context['park_slug'] = self.kwargs['park_slug']
context['park'] = self.object.park
# Add history records
context['history'] = RideEvent.objects.filter(
pgh_obj_id=self.object.id
).order_by('-pgh_created_at')
return context
class RideCreateView(LoginRequiredMixin, ParkContextRequired, CreateView):
"""View for creating a new ride"""
model = Ride
form_class = RideForm
template_name = 'rides/ride_form.html'
def get_success_url(self):
"""Get URL to redirect to after successful creation"""
return reverse('parks:rides:ride_detail', kwargs={
'park_slug': self.park.slug,
'ride_slug': self.object.slug
})
def get_form_kwargs(self):
"""Pass park to the form"""
kwargs = super().get_form_kwargs()
self.park = get_object_or_404(Park, slug=self.kwargs['park_slug'])
kwargs['park'] = self.park
return kwargs
def get_context_data(self, **kwargs):
"""Add park and park_slug to context"""
context = super().get_context_data(**kwargs)
context['park'] = self.park
context['park_slug'] = self.park.slug
context['is_edit'] = False
return context
def form_valid(self, form):
"""Handle form submission including new items"""
# Check for new manufacturer
manufacturer_name = form.cleaned_data.get('manufacturer_search')
if manufacturer_name and not form.cleaned_data.get('manufacturer'):
EditSubmission.objects.create(
user=self.request.user,
content_type=ContentType.objects.get_for_model(Manufacturer),
submission_type="CREATE",
changes={"name": manufacturer_name},
)
# Check for new designer
designer_name = form.cleaned_data.get('designer_search')
if designer_name and not form.cleaned_data.get('designer'):
EditSubmission.objects.create(
user=self.request.user,
content_type=ContentType.objects.get_for_model(Designer),
submission_type="CREATE",
changes={"name": designer_name},
)
# Check for new ride model
ride_model_name = form.cleaned_data.get('ride_model_search')
manufacturer = form.cleaned_data.get('manufacturer')
if ride_model_name and not form.cleaned_data.get('ride_model') and manufacturer:
EditSubmission.objects.create(
user=self.request.user,
content_type=ContentType.objects.get_for_model(RideModel),
submission_type="CREATE",
changes={
"name": ride_model_name,
"manufacturer": manufacturer.id
},
)
return super().form_valid(form)
class RideUpdateView(LoginRequiredMixin, ParkContextRequired, EditSubmissionMixin, UpdateView):
"""View for updating an existing ride"""
@@ -83,3 +210,129 @@ class RideUpdateView(LoginRequiredMixin, ParkContextRequired, EditSubmissionMixi
)
return super().form_valid(form)
class RideListView(ListView):
"""View for displaying a list of rides"""
model = Ride
template_name = 'rides/ride_list.html'
context_object_name = 'rides'
def get_queryset(self):
"""Get all rides or filter by park if park_slug is provided"""
queryset = Ride.objects.all().select_related(
'park',
'ride_model',
'ride_model__manufacturer'
).prefetch_related('photos')
if 'park_slug' in self.kwargs:
self.park = get_object_or_404(Park, slug=self.kwargs['park_slug'])
queryset = queryset.filter(park=self.park)
return queryset
def get_context_data(self, **kwargs):
"""Add park to context if park_slug is provided"""
context = super().get_context_data(**kwargs)
if hasattr(self, 'park'):
context['park'] = self.park
context['park_slug'] = self.kwargs['park_slug']
return context
class SingleCategoryListView(ListView):
"""View for displaying rides of a specific category"""
model = Ride
template_name = 'rides/park_category_list.html'
context_object_name = 'rides'
def get_queryset(self):
"""Get rides filtered by category and optionally by park"""
category = self.kwargs.get('category')
queryset = Ride.objects.filter(
category=category
).select_related(
'park',
'ride_model',
'ride_model__manufacturer'
)
if 'park_slug' in self.kwargs:
self.park = get_object_or_404(Park, slug=self.kwargs['park_slug'])
queryset = queryset.filter(park=self.park)
return queryset
def get_context_data(self, **kwargs):
"""Add park and category information to context"""
context = super().get_context_data(**kwargs)
if hasattr(self, 'park'):
context['park'] = self.park
context['park_slug'] = self.kwargs['park_slug']
context['category'] = dict(CATEGORY_CHOICES).get(
self.kwargs['category'])
return context
# Alias for parks app to maintain backward compatibility
ParkSingleCategoryListView = SingleCategoryListView
@login_required
def search_manufacturers(request: HttpRequest) -> HttpResponse:
"""Search manufacturers and return results for HTMX"""
query = request.GET.get("q", "").strip()
# Show all manufacturers on click, filter on input
manufacturers = Manufacturer.objects.all().order_by("name")
if query:
manufacturers = manufacturers.filter(name__icontains=query)
manufacturers = manufacturers[:10]
return render(
request,
"rides/partials/manufacturer_search_results.html",
{"manufacturers": manufacturers, "search_term": query},
)
@login_required
def search_designers(request: HttpRequest) -> HttpResponse:
"""Search designers and return results for HTMX"""
query = request.GET.get("q", "").strip()
# Show all designers on click, filter on input
designers = Designer.objects.all().order_by("name")
if query:
designers = designers.filter(name__icontains=query)
designers = designers[:10]
return render(
request,
"rides/partials/designer_search_results.html",
{"designers": designers, "search_term": query},
)
@login_required
def search_ride_models(request: HttpRequest) -> HttpResponse:
"""Search ride models and return results for HTMX"""
query = request.GET.get("q", "").strip()
manufacturer_id = request.GET.get("manufacturer")
# Show all ride models on click, filter on input
ride_models = RideModel.objects.select_related(
"manufacturer").order_by("name")
if query:
ride_models = ride_models.filter(name__icontains=query)
if manufacturer_id:
ride_models = ride_models.filter(manufacturer_id=manufacturer_id)
ride_models = ride_models[:10]
return render(
request,
"rides/partials/ride_model_search_results.html",
{"ride_models": ride_models, "search_term": query,
"manufacturer_id": manufacturer_id},
)