mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 12:11:13 -05:00
- Created ParkPhoto and ParkPhotoEvent models in the parks app, including fields for image, caption, alt text, and relationships to the Park model. - Implemented triggers for insert and update operations on ParkPhoto to log changes in ParkPhotoEvent. - Created RidePhoto and RidePhotoEvent models in the rides app, with similar structure and functionality as ParkPhoto. - Added fields for photo type in RidePhoto and implemented corresponding triggers for logging changes. - Established necessary indexes and unique constraints for both models to ensure data integrity and optimize queries.
380 lines
14 KiB
Python
380 lines
14 KiB
Python
from apps.parks.models import Park, ParkArea
|
|
from django import forms
|
|
from django.forms import ModelChoiceField
|
|
from django.urls import reverse_lazy
|
|
from .models.company import Company
|
|
from .models.rides import Ride, RideModel
|
|
|
|
Manufacturer = Company
|
|
Designer = Company
|
|
|
|
|
|
class RideForm(forms.ModelForm):
|
|
park_search = forms.CharField(
|
|
label="Park *",
|
|
required=True,
|
|
widget=forms.TextInput(
|
|
attrs={
|
|
"class": (
|
|
"w-full border-gray-300 rounded-lg form-input "
|
|
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
),
|
|
"placeholder": "Search for a park...",
|
|
"hx-get": "/parks/search/",
|
|
"hx-trigger": "click, input delay:200ms",
|
|
"hx-target": "#park-search-results",
|
|
"name": "q",
|
|
"autocomplete": "off",
|
|
}
|
|
),
|
|
)
|
|
|
|
manufacturer_search = forms.CharField(
|
|
label="Manufacturer",
|
|
required=False,
|
|
widget=forms.TextInput(
|
|
attrs={
|
|
"class": (
|
|
"w-full border-gray-300 rounded-lg form-input "
|
|
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
),
|
|
"placeholder": "Search for a manufacturer...",
|
|
"hx-get": reverse_lazy("rides:search_companies"),
|
|
"hx-trigger": "click, input delay:200ms",
|
|
"hx-target": "#manufacturer-search-results",
|
|
"name": "q",
|
|
"autocomplete": "off",
|
|
}
|
|
),
|
|
)
|
|
|
|
designer_search = forms.CharField(
|
|
label="Designer",
|
|
required=False,
|
|
widget=forms.TextInput(
|
|
attrs={
|
|
"class": (
|
|
"w-full border-gray-300 rounded-lg form-input "
|
|
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
),
|
|
"placeholder": "Search for a designer...",
|
|
"hx-get": reverse_lazy("rides:search_companies"),
|
|
"hx-trigger": "click, input delay:200ms",
|
|
"hx-target": "#designer-search-results",
|
|
"name": "q",
|
|
"autocomplete": "off",
|
|
}
|
|
),
|
|
)
|
|
|
|
ride_model_search = forms.CharField(
|
|
label="Ride Model",
|
|
required=False,
|
|
widget=forms.TextInput(
|
|
attrs={
|
|
"class": (
|
|
"w-full border-gray-300 rounded-lg form-input "
|
|
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
),
|
|
"placeholder": "Search for a ride model...",
|
|
"hx-get": reverse_lazy("rides:search_ride_models"),
|
|
"hx-trigger": "click, input delay:200ms",
|
|
"hx-target": "#ride-model-search-results",
|
|
"hx-include": "[name='manufacturer']",
|
|
"name": "q",
|
|
"autocomplete": "off",
|
|
}
|
|
),
|
|
)
|
|
|
|
park = forms.ModelChoiceField(
|
|
queryset=Park.objects.all(),
|
|
required=True,
|
|
label="",
|
|
widget=forms.HiddenInput(),
|
|
)
|
|
|
|
manufacturer = forms.ModelChoiceField(
|
|
queryset=Manufacturer.objects.all(),
|
|
required=False,
|
|
label="",
|
|
widget=forms.HiddenInput(),
|
|
)
|
|
|
|
designer = forms.ModelChoiceField(
|
|
queryset=Designer.objects.all(),
|
|
required=False,
|
|
label="",
|
|
widget=forms.HiddenInput(),
|
|
)
|
|
|
|
ride_model = forms.ModelChoiceField(
|
|
queryset=RideModel.objects.all(),
|
|
required=False,
|
|
label="",
|
|
widget=forms.HiddenInput(),
|
|
)
|
|
|
|
park_area = ModelChoiceField(
|
|
queryset=ParkArea.objects.none(),
|
|
required=False,
|
|
widget=forms.Select(
|
|
attrs={
|
|
"class": (
|
|
"w-full border-gray-300 rounded-lg form-select "
|
|
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
),
|
|
"placeholder": "Select an area within the park...",
|
|
}
|
|
),
|
|
)
|
|
|
|
class Meta:
|
|
model = Ride
|
|
fields = [
|
|
"name",
|
|
"category",
|
|
"manufacturer",
|
|
"designer",
|
|
"ride_model",
|
|
"status",
|
|
"post_closing_status",
|
|
"opening_date",
|
|
"closing_date",
|
|
"status_since",
|
|
"min_height_in",
|
|
"max_height_in",
|
|
"capacity_per_hour",
|
|
"ride_duration_seconds",
|
|
"description",
|
|
]
|
|
widgets = {
|
|
"name": forms.TextInput(
|
|
attrs={
|
|
"class": (
|
|
"w-full border-gray-300 rounded-lg form-input "
|
|
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
),
|
|
"placeholder": "Official name of the ride",
|
|
}
|
|
),
|
|
"category": forms.Select(
|
|
attrs={
|
|
"class": (
|
|
"w-full border-gray-300 rounded-lg form-select "
|
|
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
),
|
|
"hx-get": reverse_lazy("rides:coaster_fields"),
|
|
"hx-target": "#coaster-fields",
|
|
"hx-trigger": "change",
|
|
"hx-include": "this",
|
|
"hx-swap": "innerHTML",
|
|
}
|
|
),
|
|
"status": forms.Select(
|
|
attrs={
|
|
"class": (
|
|
"w-full border-gray-300 rounded-lg form-select "
|
|
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
),
|
|
"placeholder": "Current operational status",
|
|
"x-model": "status",
|
|
"@change": "handleStatusChange",
|
|
}
|
|
),
|
|
"post_closing_status": forms.Select(
|
|
attrs={
|
|
"class": (
|
|
"w-full border-gray-300 rounded-lg form-select "
|
|
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
),
|
|
"placeholder": "Status after closing",
|
|
"x-show": "status === 'CLOSING'",
|
|
}
|
|
),
|
|
"opening_date": forms.DateInput(
|
|
attrs={
|
|
"type": "date",
|
|
"class": (
|
|
"w-full border-gray-300 rounded-lg form-input "
|
|
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
),
|
|
"placeholder": "Date when ride first opened",
|
|
}
|
|
),
|
|
"closing_date": forms.DateInput(
|
|
attrs={
|
|
"type": "date",
|
|
"class": (
|
|
"w-full border-gray-300 rounded-lg form-input "
|
|
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
),
|
|
"placeholder": "Date when ride will close",
|
|
"x-show": "['CLOSING', 'SBNO', 'CLOSED_PERM', 'DEMOLISHED', 'RELOCATED'].includes(status)",
|
|
":required": "status === 'CLOSING'",
|
|
}
|
|
),
|
|
"status_since": forms.DateInput(
|
|
attrs={
|
|
"type": "date",
|
|
"class": (
|
|
"w-full border-gray-300 rounded-lg form-input "
|
|
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
),
|
|
"placeholder": "Date when current status took effect",
|
|
}
|
|
),
|
|
"min_height_in": forms.NumberInput(
|
|
attrs={
|
|
"class": (
|
|
"w-full border-gray-300 rounded-lg form-input "
|
|
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
),
|
|
"min": "0",
|
|
"placeholder": "Minimum height requirement in inches",
|
|
}
|
|
),
|
|
"max_height_in": forms.NumberInput(
|
|
attrs={
|
|
"class": (
|
|
"w-full border-gray-300 rounded-lg form-input "
|
|
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
),
|
|
"min": "0",
|
|
"placeholder": "Maximum height limit in inches (if applicable)",
|
|
}
|
|
),
|
|
"capacity_per_hour": forms.NumberInput(
|
|
attrs={
|
|
"class": (
|
|
"w-full border-gray-300 rounded-lg form-input "
|
|
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
),
|
|
"min": "0",
|
|
"placeholder": "Theoretical hourly ride capacity",
|
|
}
|
|
),
|
|
"ride_duration_seconds": forms.NumberInput(
|
|
attrs={
|
|
"class": (
|
|
"w-full border-gray-300 rounded-lg form-input "
|
|
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
),
|
|
"min": "0",
|
|
"placeholder": "Total duration of one ride cycle in seconds",
|
|
}
|
|
),
|
|
"description": forms.Textarea(
|
|
attrs={
|
|
"rows": 4,
|
|
"class": (
|
|
"w-full border-gray-300 rounded-lg form-textarea "
|
|
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
),
|
|
"placeholder": "General description and notable features of the ride",
|
|
}
|
|
),
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
park = kwargs.pop("park", None)
|
|
super().__init__(*args, **kwargs)
|
|
|
|
# Make category required
|
|
self.fields["category"].required = True
|
|
|
|
# Clear any default values for date fields
|
|
self.fields["opening_date"].initial = None
|
|
self.fields["closing_date"].initial = None
|
|
self.fields["status_since"].initial = None
|
|
|
|
# Move fields to the beginning in desired order
|
|
field_order = [
|
|
"park_search",
|
|
"park",
|
|
"park_area",
|
|
"name",
|
|
"manufacturer_search",
|
|
"manufacturer",
|
|
"designer_search",
|
|
"designer",
|
|
"ride_model_search",
|
|
"ride_model",
|
|
"category",
|
|
"status",
|
|
"post_closing_status",
|
|
"opening_date",
|
|
"closing_date",
|
|
"status_since",
|
|
"min_height_in",
|
|
"max_height_in",
|
|
"capacity_per_hour",
|
|
"ride_duration_seconds",
|
|
"description",
|
|
]
|
|
self.order_fields(field_order)
|
|
|
|
if park:
|
|
# If park is provided, set it as the initial value
|
|
self.fields["park"].initial = park
|
|
# Hide the park search field since we know the park
|
|
del self.fields["park_search"]
|
|
# Create new park_area field with park's areas
|
|
self.fields["park_area"] = forms.ModelChoiceField(
|
|
queryset=park.areas.all(),
|
|
required=False,
|
|
widget=forms.Select(
|
|
attrs={
|
|
"class": (
|
|
"w-full border-gray-300 rounded-lg form-select "
|
|
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
),
|
|
"placeholder": "Select an area within the park...",
|
|
}
|
|
),
|
|
)
|
|
else:
|
|
# If no park provided, show park search and disable park_area until
|
|
# park is selected
|
|
self.fields["park_area"].widget.attrs["disabled"] = True
|
|
# Initialize park search with current park name if editing
|
|
if self.instance and self.instance.pk and self.instance.park:
|
|
self.fields["park_search"].initial = self.instance.park.name
|
|
self.fields["park"].initial = self.instance.park
|
|
|
|
# Initialize manufacturer, designer, and ride model search fields if
|
|
# editing
|
|
if self.instance and self.instance.pk:
|
|
if self.instance.manufacturer:
|
|
self.fields[
|
|
"manufacturer_search"
|
|
].initial = self.instance.manufacturer.name
|
|
self.fields["manufacturer"].initial = self.instance.manufacturer
|
|
if self.instance.designer:
|
|
self.fields["designer_search"].initial = self.instance.designer.name
|
|
self.fields["designer"].initial = self.instance.designer
|
|
if self.instance.ride_model:
|
|
self.fields["ride_model_search"].initial = self.instance.ride_model.name
|
|
self.fields["ride_model"].initial = self.instance.ride_model
|
|
|
|
|
|
class RideSearchForm(forms.Form):
|
|
"""Form for searching rides with HTMX autocomplete."""
|
|
|
|
ride = forms.ModelChoiceField(
|
|
queryset=Ride.objects.all(),
|
|
label="Find a ride",
|
|
required=False,
|
|
widget=forms.Select(
|
|
attrs={
|
|
"class": (
|
|
"w-full border-gray-300 rounded-lg form-input "
|
|
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
|
),
|
|
"hx-get": reverse_lazy("rides:search"),
|
|
"hx-trigger": "change",
|
|
"hx-target": "#ride-search-results",
|
|
}
|
|
),
|
|
)
|