Files
thrillwiki_django_no_react/rides/views.py

388 lines
15 KiB
Python

from django.views.generic import DetailView, ListView, CreateView, UpdateView
from django.shortcuts import get_object_or_404
from django.core.serializers.json import DjangoJSONEncoder
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.contrib import messages
from django.http import JsonResponse, HttpResponseRedirect, Http404
from django.db.models import Count
from .models import Ride, RollerCoasterStats
from .forms import RideForm
from parks.models import Park
from core.views import SlugRedirectMixin
from moderation.mixins import EditSubmissionMixin, PhotoSubmissionMixin, HistoryMixin
from media.models import Photo
class SingleCategoryListView(ListView):
model = Ride
template_name = 'rides/ride_category_list.html'
context_object_name = 'categories'
def get_category_code(self):
category = self.kwargs.get('category')
if not category:
raise Http404("Category not found")
return category
def get_queryset(self):
category_code = self.get_category_code()
category_name = dict(Ride.CATEGORY_CHOICES)[category_code]
rides = Ride.objects.filter(category=category_code).select_related(
'park', 'manufacturer'
).order_by('name')
return {category_name: rides} if rides.exists() else {}
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
category_code = self.get_category_code()
category_name = dict(Ride.CATEGORY_CHOICES)[category_code]
context['title'] = f'All {category_name}s'
context['category_code'] = category_code
return context
class ParkSingleCategoryListView(ListView):
model = Ride
template_name = 'rides/ride_category_list.html'
context_object_name = 'categories'
def setup(self, request, *args, **kwargs):
super().setup(request, *args, **kwargs)
self.park = get_object_or_404(Park, slug=self.kwargs['park_slug'])
def get_category_code(self):
category = self.kwargs.get('category')
if not category:
raise Http404("Category not found")
return category
def get_queryset(self):
category_code = self.get_category_code()
category_name = dict(Ride.CATEGORY_CHOICES)[category_code]
rides = Ride.objects.filter(
park=self.park,
category=category_code
).select_related('manufacturer').order_by('name')
return {category_name: rides} if rides.exists() else {}
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['park'] = self.park
category_code = self.get_category_code()
category_name = dict(Ride.CATEGORY_CHOICES)[category_code]
context['title'] = f'{category_name}s at {self.park.name}'
context['category_code'] = category_code
return context
class RideCreateView(LoginRequiredMixin, CreateView):
model = Ride
form_class = RideForm
template_name = 'rides/ride_form.html'
def setup(self, request, *args, **kwargs):
super().setup(request, *args, **kwargs)
self.park = get_object_or_404(Park, slug=self.kwargs['park_slug'])
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['park'] = self.park
return kwargs
def form_valid(self, form):
form.instance.park = self.park
cleaned_data = form.cleaned_data.copy()
cleaned_data['park'] = self.park.id
# Convert model instances to IDs for JSON serialization
if cleaned_data.get('park_area'):
cleaned_data['park_area'] = cleaned_data['park_area'].id
if cleaned_data.get('manufacturer'):
cleaned_data['manufacturer'] = cleaned_data['manufacturer'].id
# Create submission record
submission = EditSubmission.objects.create(
user=self.request.user,
content_type=ContentType.objects.get_for_model(Ride),
submission_type='CREATE',
changes=cleaned_data,
reason=self.request.POST.get('reason', ''),
source=self.request.POST.get('source', '')
)
# If user is moderator or above, auto-approve
if self.request.user.role in ['MODERATOR', 'ADMIN', 'SUPERUSER']:
try:
self.object = form.save()
submission.object_id = self.object.id
submission.status = 'APPROVED'
submission.handled_by = self.request.user
submission.save()
# Handle photo uploads
photos = self.request.FILES.getlist('photos')
uploaded_count = 0
for photo_file in photos:
try:
Photo.objects.create(
image=photo_file,
uploaded_by=self.request.user,
content_type=ContentType.objects.get_for_model(Ride),
object_id=self.object.id
)
uploaded_count += 1
except Exception as e:
messages.error(self.request, f"Error uploading photo {photo_file.name}: {str(e)}")
messages.success(
self.request,
f"Successfully created {self.object.name} at {self.park.name}. "
f"Added {uploaded_count} photo(s)."
)
return HttpResponseRedirect(self.get_success_url())
except Exception as e:
messages.error(
self.request,
f"Error creating ride: {str(e)}. Please check your input and try again."
)
return self.form_invalid(form)
messages.success(
self.request,
"Your ride submission has been sent for review. "
"You will be notified when it is approved."
)
return HttpResponseRedirect(reverse('parks:rides:ride_list', kwargs={'park_slug': self.park.slug}))
def form_invalid(self, form):
messages.error(
self.request,
"Please correct the errors below. Required fields are marked with an asterisk (*)."
)
for field, errors in form.errors.items():
for error in errors:
messages.error(self.request, f"{field}: {error}")
return super().form_invalid(form)
def get_success_url(self):
return reverse('parks:rides:ride_detail', kwargs={
'park_slug': self.park.slug,
'ride_slug': self.object.slug
})
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['park'] = self.park
return context
class RideUpdateView(LoginRequiredMixin, UpdateView):
model = Ride
form_class = RideForm
template_name = 'rides/ride_form.html'
slug_url_kwarg = 'ride_slug'
def setup(self, request, *args, **kwargs):
super().setup(request, *args, **kwargs)
self.park = get_object_or_404(Park, slug=self.kwargs['park_slug'])
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['park'] = self.park
return kwargs
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['park'] = self.park
context['is_edit'] = True
return context
def form_valid(self, form):
cleaned_data = form.cleaned_data.copy()
cleaned_data['park'] = self.park.id
# Convert model instances to IDs for JSON serialization
if cleaned_data.get('park_area'):
cleaned_data['park_area'] = cleaned_data['park_area'].id
if cleaned_data.get('manufacturer'):
cleaned_data['manufacturer'] = cleaned_data['manufacturer'].id
# Create submission record
submission = EditSubmission.objects.create(
user=self.request.user,
content_type=ContentType.objects.get_for_model(Ride),
object_id=self.object.id,
submission_type='EDIT',
changes=cleaned_data,
reason=self.request.POST.get('reason', ''),
source=self.request.POST.get('source', '')
)
# If user is moderator or above, auto-approve
if self.request.user.role in ['MODERATOR', 'ADMIN', 'SUPERUSER']:
try:
self.object = form.save()
submission.status = 'APPROVED'
submission.handled_by = self.request.user
submission.save()
# Handle photo uploads
photos = self.request.FILES.getlist('photos')
uploaded_count = 0
for photo_file in photos:
try:
Photo.objects.create(
image=photo_file,
uploaded_by=self.request.user,
content_type=ContentType.objects.get_for_model(Ride),
object_id=self.object.id
)
uploaded_count += 1
except Exception as e:
messages.error(self.request, f"Error uploading photo {photo_file.name}: {str(e)}")
messages.success(
self.request,
f"Successfully updated {self.object.name}. "
f"Added {uploaded_count} new photo(s)."
)
return HttpResponseRedirect(self.get_success_url())
except Exception as e:
messages.error(
self.request,
f"Error updating ride: {str(e)}. Please check your input and try again."
)
return self.form_invalid(form)
messages.success(
self.request,
f"Your changes to {self.object.name} have been sent for review. "
"You will be notified when they are approved."
)
return HttpResponseRedirect(reverse('parks:rides:ride_detail', kwargs={
'park_slug': self.park.slug,
'ride_slug': self.object.slug
}))
def form_invalid(self, form):
messages.error(
self.request,
"Please correct the errors below. Required fields are marked with an asterisk (*)."
)
for field, errors in form.errors.items():
for error in errors:
messages.error(self.request, f"{field}: {error}")
return super().form_invalid(form)
def get_success_url(self):
return reverse('parks:rides:ride_detail', kwargs={
'park_slug': self.park.slug,
'ride_slug': self.object.slug
})
class RideDetailView(SlugRedirectMixin, EditSubmissionMixin, PhotoSubmissionMixin, HistoryMixin, DetailView):
model = Ride
template_name = 'rides/ride_detail.html'
context_object_name = 'ride'
slug_url_kwarg = 'ride_slug'
def get_object(self, queryset=None):
if queryset is None:
queryset = self.get_queryset()
park_slug = self.kwargs.get('park_slug')
ride_slug = self.kwargs.get('ride_slug')
# Try to get by current or historical slug
obj, is_old_slug = self.model.get_by_slug(ride_slug)
if obj.park.slug != park_slug:
raise self.model.DoesNotExist("Park slug doesn't match")
return obj
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.object.category == 'RC':
context['coaster_stats'] = RollerCoasterStats.objects.filter(ride=self.object).first()
return context
def get_redirect_url_pattern(self):
return 'parks:rides:ride_detail'
def get_redirect_url_kwargs(self):
return {
'park_slug': self.object.park.slug,
'ride_slug': self.object.slug
}
class RideListView(ListView):
model = Ride
template_name = 'rides/ride_list.html'
context_object_name = 'rides'
def setup(self, request, *args, **kwargs):
super().setup(request, *args, **kwargs)
self.park = None
if 'park_slug' in self.kwargs:
self.park = get_object_or_404(Park, slug=self.kwargs['park_slug'])
def get_queryset(self):
queryset = Ride.objects.select_related('park', 'coaster_stats', 'manufacturer').prefetch_related('photos')
# Filter by park if viewing park-specific rides
if self.park:
queryset = queryset.filter(park=self.park)
search = self.request.GET.get('search', '').strip() or None
category = self.request.GET.get('category', '').strip() or None
status = self.request.GET.get('status', '').strip() or None
manufacturer = self.request.GET.get('manufacturer', '').strip() or None
if search:
if self.park:
queryset = queryset.filter(name__icontains=search)
else:
queryset = queryset.filter(
Q(name__icontains=search) |
Q(park__name__icontains=search)
)
if category:
queryset = queryset.filter(category=category)
if status:
queryset = queryset.filter(status=status)
if manufacturer:
queryset = queryset.exclude(manufacturer__isnull=True)
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['park'] = self.park
# Get manufacturers for the filter dropdown
manufacturer_query = Ride.objects
if self.park:
manufacturer_query = manufacturer_query.filter(park=self.park)
context['manufacturers'] = list(
manufacturer_query.exclude(manufacturer__isnull=True)
.values_list('manufacturer__name', flat=True)
.distinct().order_by('manufacturer__name')
)
# Add current filter values to context
context['current_filters'] = {
'search': self.request.GET.get('search', ''),
'category': self.request.GET.get('category', ''),
'status': self.request.GET.get('status', ''),
'manufacturer': self.request.GET.get('manufacturer', '')
}
return context
def get(self, request, *args, **kwargs):
# Check if this is an HTMX request
if request.htmx:
# If it is, return just the rides list partial
self.template_name = 'rides/partials/ride_list.html'
return super().get(request, *args, **kwargs)