mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 06:51:08 -05:00
feat: complete monorepo structure with frontend and shared resources
- Add complete backend/ directory with full Django application - Add frontend/ directory with Vite + TypeScript setup ready for Next.js - Add comprehensive shared/ directory with: - Complete documentation and memory-bank archives - Media files and avatars (letters, park/ride images) - Deployment scripts and automation tools - Shared types and utilities - Add architecture/ directory with migration guides - Configure pnpm workspace for monorepo development - Update .gitignore to exclude .django_tailwind_cli/ build artifacts - Preserve all historical documentation in shared/docs/memory-bank/ - Set up proper structure for full-stack development with shared resources
This commit is contained in:
454
backend/apps/rides/views.py
Normal file
454
backend/apps/rides/views.py
Normal file
@@ -0,0 +1,454 @@
|
||||
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.db.models import Q
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.http import HttpRequest, HttpResponse, Http404
|
||||
from django.db.models import Count
|
||||
from .models.rides import Ride, RideModel, Categories
|
||||
from .models.company import Company
|
||||
from .forms import RideForm, RideSearchForm
|
||||
from apps.parks.models import Park
|
||||
from apps.moderation.mixins import EditSubmissionMixin, HistoryMixin
|
||||
from apps.moderation.models import EditSubmission
|
||||
|
||||
|
||||
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
|
||||
|
||||
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(Company),
|
||||
submission_type="CREATE",
|
||||
changes={"name": manufacturer_name, "roles": ["MANUFACTURER"]},
|
||||
)
|
||||
|
||||
# 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(Company),
|
||||
submission_type="CREATE",
|
||||
changes={"name": designer_name, "roles": ["DESIGNER"]},
|
||||
)
|
||||
|
||||
# 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"""
|
||||
|
||||
model = Ride
|
||||
form_class = RideForm
|
||||
template_name = "rides/ride_form.html"
|
||||
slug_url_kwarg = "ride_slug"
|
||||
|
||||
def get_success_url(self):
|
||||
"""Get URL to redirect to after successful update"""
|
||||
return reverse(
|
||||
"parks:rides:ride_detail",
|
||||
kwargs={
|
||||
"park_slug": self.park.slug,
|
||||
"ride_slug": self.object.slug,
|
||||
},
|
||||
)
|
||||
|
||||
def get_queryset(self):
|
||||
"""Get ride for the specific park"""
|
||||
return Ride.objects.filter(park__slug=self.kwargs["park_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"] = True
|
||||
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(Company),
|
||||
submission_type="CREATE",
|
||||
changes={"name": manufacturer_name, "roles": ["MANUFACTURER"]},
|
||||
)
|
||||
|
||||
# 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(Company),
|
||||
submission_type="CREATE",
|
||||
changes={"name": designer_name, "roles": ["DESIGNER"]},
|
||||
)
|
||||
|
||||
# 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 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 filtered rides based on search and filters"""
|
||||
queryset = (
|
||||
Ride.objects.all()
|
||||
.select_related("park", "ride_model", "ride_model__manufacturer")
|
||||
.prefetch_related("photos")
|
||||
)
|
||||
|
||||
# Park filter
|
||||
if "park_slug" in self.kwargs:
|
||||
self.park = get_object_or_404(Park, slug=self.kwargs["park_slug"])
|
||||
queryset = queryset.filter(park=self.park)
|
||||
|
||||
# Search term handling
|
||||
search = self.request.GET.get("q", "").strip()
|
||||
if search:
|
||||
# Split search terms for more flexible matching
|
||||
search_terms = search.split()
|
||||
search_query = Q()
|
||||
|
||||
for term in search_terms:
|
||||
term_query = (
|
||||
Q(name__icontains=term)
|
||||
| Q(park__name__icontains=term)
|
||||
| Q(description__icontains=term)
|
||||
)
|
||||
search_query &= term_query
|
||||
|
||||
queryset = queryset.filter(search_query)
|
||||
|
||||
# Category filter
|
||||
category = self.request.GET.get("category")
|
||||
if category and category != "all":
|
||||
queryset = queryset.filter(category=category)
|
||||
|
||||
# Operating status filter
|
||||
if self.request.GET.get("operating") == "true":
|
||||
queryset = queryset.filter(status="operating")
|
||||
|
||||
return queryset
|
||||
|
||||
def get_template_names(self):
|
||||
"""Return appropriate template based on request type"""
|
||||
if self.request.htmx:
|
||||
return ["rides/partials/ride_list_results.html"]
|
||||
return [self.template_name]
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Add park and category choices 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_choices"] = Categories
|
||||
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(Categories).get(self.kwargs["category"])
|
||||
return context
|
||||
|
||||
|
||||
# Alias for parks app to maintain backward compatibility
|
||||
ParkSingleCategoryListView = SingleCategoryListView
|
||||
|
||||
|
||||
def search_companies(request: HttpRequest) -> HttpResponse:
|
||||
"""Search companies and return results for HTMX"""
|
||||
query = request.GET.get("q", "").strip()
|
||||
role = request.GET.get("role", "").upper()
|
||||
|
||||
companies = Company.objects.all().order_by("name")
|
||||
if role:
|
||||
companies = companies.filter(roles__contains=[role])
|
||||
if query:
|
||||
companies = companies.filter(name__icontains=query)
|
||||
companies = companies[:10]
|
||||
|
||||
return render(
|
||||
request,
|
||||
"rides/partials/company_search_results.html",
|
||||
{"companies": companies, "search_term": query},
|
||||
)
|
||||
|
||||
|
||||
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,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def get_search_suggestions(request: HttpRequest) -> HttpResponse:
|
||||
"""Get smart search suggestions for rides
|
||||
|
||||
Returns suggestions including:
|
||||
- Common matching ride names
|
||||
- Matching parks
|
||||
- Matching categories
|
||||
"""
|
||||
query = request.GET.get("q", "").strip().lower()
|
||||
suggestions = []
|
||||
|
||||
if query:
|
||||
# Get common ride names
|
||||
matching_names = (
|
||||
Ride.objects.filter(name__icontains=query)
|
||||
.values("name")
|
||||
.annotate(count=Count("id"))
|
||||
.order_by("-count")[:3]
|
||||
)
|
||||
|
||||
for match in matching_names:
|
||||
suggestions.append(
|
||||
{
|
||||
"type": "ride",
|
||||
"text": match["name"],
|
||||
"count": match["count"],
|
||||
}
|
||||
)
|
||||
|
||||
# Get matching parks
|
||||
matching_parks = Park.objects.filter(
|
||||
Q(name__icontains=query) | Q(location__city__icontains=query)
|
||||
)[:3]
|
||||
|
||||
for park in matching_parks:
|
||||
suggestions.append(
|
||||
{
|
||||
"type": "park",
|
||||
"text": park.name,
|
||||
"location": park.location.city if park.location else None,
|
||||
}
|
||||
)
|
||||
|
||||
# Add category matches
|
||||
for code, name in Categories:
|
||||
if query in name.lower():
|
||||
ride_count = Ride.objects.filter(category=code).count()
|
||||
suggestions.append(
|
||||
{
|
||||
"type": "category",
|
||||
"code": code,
|
||||
"text": name,
|
||||
"count": ride_count,
|
||||
}
|
||||
)
|
||||
|
||||
return render(
|
||||
request,
|
||||
"rides/partials/search_suggestions.html",
|
||||
{"suggestions": suggestions, "query": query},
|
||||
)
|
||||
|
||||
|
||||
class RideSearchView(ListView):
|
||||
"""View for ride search functionality with HTMX support."""
|
||||
|
||||
model = Ride
|
||||
template_name = "search/partials/ride_search_results.html"
|
||||
context_object_name = "rides"
|
||||
paginate_by = 20
|
||||
|
||||
def get_queryset(self):
|
||||
"""Get filtered rides based on search form."""
|
||||
queryset = Ride.objects.select_related("park").order_by("name")
|
||||
|
||||
# Process search form
|
||||
form = RideSearchForm(self.request.GET)
|
||||
if form.is_valid():
|
||||
ride = form.cleaned_data.get("ride")
|
||||
if ride:
|
||||
# If specific ride selected, return just that ride
|
||||
queryset = queryset.filter(id=ride.id)
|
||||
else:
|
||||
# If no specific ride, filter by search term
|
||||
search_term = self.request.GET.get("ride", "").strip()
|
||||
if search_term:
|
||||
queryset = queryset.filter(name__icontains=search_term)
|
||||
|
||||
return queryset
|
||||
|
||||
def get_template_names(self):
|
||||
"""Return appropriate template based on request type."""
|
||||
if self.request.htmx:
|
||||
return ["search/partials/ride_search_results.html"]
|
||||
return ["search/ride_search.html"]
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Add search form to context."""
|
||||
context = super().get_context_data(**kwargs)
|
||||
context["search_form"] = RideSearchForm(self.request.GET)
|
||||
return context
|
||||
Reference in New Issue
Block a user