mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 07:31:07 -05:00
Add ParkContextRequired mixin to enforce park context in ride views; update URLs and templates for global ride listing
This commit is contained in:
@@ -7,7 +7,7 @@ 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
|
||||
from django.http import HttpRequest, HttpResponse, Http404
|
||||
from django.db.models import Count
|
||||
from .models import (
|
||||
Ride, RollerCoasterStats, RideModel, RideEvent,
|
||||
@@ -21,6 +21,13 @@ from moderation.models import EditSubmission
|
||||
from companies.models import Manufacturer
|
||||
from designers.models import Designer
|
||||
|
||||
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')
|
||||
@@ -61,7 +68,7 @@ class RideDetailView(HistoryMixin, DetailView):
|
||||
|
||||
return context
|
||||
|
||||
class RideCreateView(LoginRequiredMixin, CreateView):
|
||||
class RideCreateView(LoginRequiredMixin, ParkContextRequired, CreateView):
|
||||
"""View for creating a new ride"""
|
||||
model = Ride
|
||||
form_class = RideForm
|
||||
@@ -69,27 +76,23 @@ class RideCreateView(LoginRequiredMixin, CreateView):
|
||||
|
||||
def get_success_url(self):
|
||||
"""Get URL to redirect to after successful creation"""
|
||||
if hasattr(self, 'park'):
|
||||
return reverse('parks:rides:ride_detail', kwargs={
|
||||
'park_slug': self.park.slug,
|
||||
'ride_slug': self.object.slug
|
||||
})
|
||||
return reverse('rides:ride_detail', kwargs={'ride_slug': self.object.slug})
|
||||
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()
|
||||
if 'park_slug' in self.kwargs:
|
||||
self.park = get_object_or_404(Park, slug=self.kwargs['park_slug'])
|
||||
kwargs['park'] = self.park
|
||||
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)
|
||||
if hasattr(self, 'park'):
|
||||
context['park'] = self.park
|
||||
context['park_slug'] = self.park.slug
|
||||
context['park'] = self.park
|
||||
context['park_slug'] = self.park.slug
|
||||
context['is_edit'] = False
|
||||
return context
|
||||
|
||||
@@ -131,7 +134,7 @@ class RideCreateView(LoginRequiredMixin, CreateView):
|
||||
|
||||
return super().form_valid(form)
|
||||
|
||||
class RideUpdateView(LoginRequiredMixin, EditSubmissionMixin, UpdateView):
|
||||
class RideUpdateView(LoginRequiredMixin, ParkContextRequired, EditSubmissionMixin, UpdateView):
|
||||
"""View for updating an existing ride"""
|
||||
model = Ride
|
||||
form_class = RideForm
|
||||
@@ -140,39 +143,27 @@ class RideUpdateView(LoginRequiredMixin, EditSubmissionMixin, UpdateView):
|
||||
|
||||
def get_success_url(self):
|
||||
"""Get URL to redirect to after successful update"""
|
||||
if hasattr(self, 'park'):
|
||||
return reverse('parks:rides:ride_detail', kwargs={
|
||||
'park_slug': self.park.slug,
|
||||
'ride_slug': self.object.slug
|
||||
})
|
||||
return reverse('rides:ride_detail', kwargs={'ride_slug': self.object.slug})
|
||||
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 if park_slug is provided"""
|
||||
queryset = Ride.objects.all()
|
||||
if 'park_slug' in self.kwargs:
|
||||
queryset = queryset.filter(park__slug=self.kwargs['park_slug'])
|
||||
return queryset
|
||||
"""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()
|
||||
# For park-specific URLs, use the park from the URL
|
||||
if 'park_slug' in self.kwargs:
|
||||
self.park = get_object_or_404(Park, slug=self.kwargs['park_slug'])
|
||||
kwargs['park'] = self.park
|
||||
# For global URLs, use the ride's park
|
||||
else:
|
||||
self.park = self.get_object().park
|
||||
kwargs['park'] = self.park
|
||||
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)
|
||||
if hasattr(self, 'park'):
|
||||
context['park'] = self.park
|
||||
context['park_slug'] = self.park.slug
|
||||
context['park'] = self.park
|
||||
context['park_slug'] = self.park.slug
|
||||
context['is_edit'] = True
|
||||
return context
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
<i class="fas fa-map-marker-alt"></i>
|
||||
<span>Parks</span>
|
||||
</a>
|
||||
<a href="{% url 'rides:ride_list' %}" class="nav-link">
|
||||
<a href="{% url 'rides:global_ride_list' %}" class="nav-link">
|
||||
<i class="fas fa-rocket"></i>
|
||||
<span>Rides</span>
|
||||
</a>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
class="px-8 py-3 text-lg btn-primary">
|
||||
Explore Parks
|
||||
</a>
|
||||
<a href="{% url 'rides:ride_list' %}"
|
||||
<a href="{% url 'rides:global_ride_list' %}"
|
||||
class="px-8 py-3 text-lg btn-secondary">
|
||||
View Rides
|
||||
</a>
|
||||
@@ -40,7 +40,7 @@
|
||||
</a>
|
||||
|
||||
<!-- Total Attractions -->
|
||||
<a href="{% url 'rides:ride_list' %}"
|
||||
<a href="{% url 'rides:global_ride_list' %}"
|
||||
class="flex flex-col items-center justify-center p-6 text-center transition-transform transform bg-white rounded-lg shadow-lg dark:bg-gray-800 hover:-translate-y-1 hover:shadow-xl">
|
||||
<div class="mb-2 text-4xl font-bold text-blue-600 dark:text-blue-400">
|
||||
{{ stats.ride_count }}
|
||||
@@ -51,7 +51,7 @@
|
||||
</a>
|
||||
|
||||
<!-- Total Roller Coasters -->
|
||||
<a href="{% url 'rides:roller_coasters' %}"
|
||||
<a href="{% url 'rides:global_roller_coasters' %}"
|
||||
class="flex flex-col items-center justify-center p-6 text-center transition-transform transform bg-white rounded-lg shadow-lg dark:bg-gray-800 hover:-translate-y-1 hover:shadow-xl">
|
||||
<div class="mb-2 text-4xl font-bold text-blue-600 dark:text-blue-400">
|
||||
{{ stats.coaster_count }}
|
||||
@@ -108,7 +108,7 @@
|
||||
</h2>
|
||||
<div class="space-y-4">
|
||||
{% for ride in popular_rides %}
|
||||
<a href="{% url 'rides:ride_detail' ride.slug %}"
|
||||
<a href="{% url 'parks:rides:ride_detail' ride.park.slug ride.slug %}"
|
||||
class="relative block h-48 overflow-hidden transition-all rounded-lg group hover:-translate-y-1 hover:shadow-xl"
|
||||
{% if ride.photos.first %}
|
||||
style="background: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.7)), url('{{ ride.photos.first.image.url }}') center/cover no-repeat;"
|
||||
@@ -147,7 +147,7 @@
|
||||
{% for item in highest_rated %}
|
||||
{% if item.park %}
|
||||
<!-- This is a ride -->
|
||||
<a href="{% url 'rides:ride_detail' item.slug %}"
|
||||
<a href="{% url 'parks:rides:ride_detail' item.park.slug item.slug %}"
|
||||
class="relative block h-48 overflow-hidden transition-all rounded-lg group hover:-translate-y-1 hover:shadow-xl"
|
||||
{% if item.photos.first %}
|
||||
style="background: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.7)), url('{{ item.photos.first.image.url }}') center/cover no-repeat;"
|
||||
|
||||
@@ -32,12 +32,7 @@
|
||||
Add Ride
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{% url 'rides:ride_create' %}" class="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path>
|
||||
</svg>
|
||||
Add Ride
|
||||
</a>
|
||||
<!-- No add ride button in global view - rides must be added from park pages -->
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -45,7 +40,7 @@
|
||||
<!-- Filters -->
|
||||
<div class="p-4 mb-6 bg-white rounded-lg shadow dark:bg-gray-800">
|
||||
<form class="grid grid-cols-1 gap-4 md:grid-cols-3"
|
||||
hx-get="{% if park %}{% url 'parks:rides:ride_list' park.slug %}{% else %}{% url 'rides:ride_list' %}{% endif %}"
|
||||
hx-get="{% if park %}{% url 'parks:rides:ride_list' park.slug %}{% else %}{% url 'rides:global_ride_list' %}{% endif %}"
|
||||
hx-trigger="change from:select, input from:input[type='text']"
|
||||
hx-target="#rides-grid"
|
||||
hx-push-url="true">
|
||||
|
||||
Reference in New Issue
Block a user