mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 10:51:09 -05:00
remove backend
This commit is contained in:
351
apps/parks/forms.py
Normal file
351
apps/parks/forms.py
Normal file
@@ -0,0 +1,351 @@
|
||||
from django import forms
|
||||
from decimal import Decimal, InvalidOperation, ROUND_DOWN
|
||||
from autocomplete.core import register
|
||||
from autocomplete.shortcuts import ModelAutocomplete
|
||||
from autocomplete.widgets import AutocompleteWidget
|
||||
from .models import Park
|
||||
from .models.location import ParkLocation
|
||||
|
||||
|
||||
@register
|
||||
class ParkAutocomplete(ModelAutocomplete):
|
||||
"""Autocomplete for searching parks.
|
||||
|
||||
Features:
|
||||
- Name-based search with partial matching
|
||||
- Prefetches related owner data
|
||||
- Applies standard park queryset filtering
|
||||
- Includes park status and location in results
|
||||
"""
|
||||
|
||||
model = Park
|
||||
search_attrs = ["name"] # We'll match on park names
|
||||
|
||||
|
||||
class ParkSearchForm(forms.Form):
|
||||
"""Form for searching parks with autocomplete."""
|
||||
|
||||
park = forms.ModelChoiceField(
|
||||
queryset=Park.objects.all(),
|
||||
required=False,
|
||||
widget=AutocompleteWidget(
|
||||
ac_class=ParkAutocomplete,
|
||||
attrs={
|
||||
"class": (
|
||||
"w-full border-gray-300 rounded-lg form-input "
|
||||
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
||||
),
|
||||
"placeholder": "Search parks...",
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class ParkForm(forms.ModelForm):
|
||||
"""Form for creating and updating Park objects with location support"""
|
||||
|
||||
# Location fields
|
||||
latitude = forms.DecimalField(
|
||||
max_digits=9,
|
||||
decimal_places=6,
|
||||
required=False,
|
||||
widget=forms.HiddenInput(),
|
||||
)
|
||||
longitude = forms.DecimalField(
|
||||
max_digits=10,
|
||||
decimal_places=6,
|
||||
required=False,
|
||||
widget=forms.HiddenInput(),
|
||||
)
|
||||
street_address = forms.CharField(
|
||||
max_length=255,
|
||||
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"
|
||||
)
|
||||
}
|
||||
),
|
||||
)
|
||||
city = forms.CharField(
|
||||
max_length=255,
|
||||
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"
|
||||
)
|
||||
}
|
||||
),
|
||||
)
|
||||
state = forms.CharField(
|
||||
max_length=255,
|
||||
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"
|
||||
)
|
||||
}
|
||||
),
|
||||
)
|
||||
country = forms.CharField(
|
||||
max_length=255,
|
||||
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"
|
||||
)
|
||||
}
|
||||
),
|
||||
)
|
||||
postal_code = forms.CharField(
|
||||
max_length=20,
|
||||
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"
|
||||
)
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Park
|
||||
fields = [
|
||||
"name",
|
||||
"description",
|
||||
"operator",
|
||||
"property_owner",
|
||||
"status",
|
||||
"opening_date",
|
||||
"closing_date",
|
||||
"operating_season",
|
||||
"size_acres",
|
||||
"website",
|
||||
# Location fields handled separately
|
||||
"latitude",
|
||||
"longitude",
|
||||
"street_address",
|
||||
"city",
|
||||
"state",
|
||||
"country",
|
||||
"postal_code",
|
||||
]
|
||||
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"
|
||||
)
|
||||
}
|
||||
),
|
||||
"description": forms.Textarea(
|
||||
attrs={
|
||||
"class": (
|
||||
"w-full border-gray-300 rounded-lg form-textarea "
|
||||
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
||||
),
|
||||
"rows": 2,
|
||||
}
|
||||
),
|
||||
"operator": forms.Select(
|
||||
attrs={
|
||||
"class": (
|
||||
"w-full border-gray-300 rounded-lg form-select "
|
||||
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
||||
)
|
||||
}
|
||||
),
|
||||
"property_owner": forms.Select(
|
||||
attrs={
|
||||
"class": (
|
||||
"w-full border-gray-300 rounded-lg form-select "
|
||||
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
||||
)
|
||||
}
|
||||
),
|
||||
"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"
|
||||
)
|
||||
}
|
||||
),
|
||||
"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"
|
||||
),
|
||||
}
|
||||
),
|
||||
"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"
|
||||
),
|
||||
}
|
||||
),
|
||||
"operating_season": 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": "e.g., Year-round, Summer only, etc.",
|
||||
}
|
||||
),
|
||||
"size_acres": forms.NumberInput(
|
||||
attrs={
|
||||
"class": (
|
||||
"w-full border-gray-300 rounded-lg form-input "
|
||||
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
||||
),
|
||||
"step": "0.01",
|
||||
"min": "0",
|
||||
}
|
||||
),
|
||||
"website": forms.URLInput(
|
||||
attrs={
|
||||
"class": (
|
||||
"w-full border-gray-300 rounded-lg form-input "
|
||||
"dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
||||
),
|
||||
"placeholder": "https://example.com",
|
||||
}
|
||||
),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
# Pre-fill location fields if editing existing park
|
||||
if self.instance and self.instance.pk and self.instance.location.exists():
|
||||
location = self.instance.location.first()
|
||||
self.fields["latitude"].initial = location.latitude
|
||||
self.fields["longitude"].initial = location.longitude
|
||||
self.fields["street_address"].initial = location.street_address
|
||||
self.fields["city"].initial = location.city
|
||||
self.fields["state"].initial = location.state
|
||||
self.fields["country"].initial = location.country
|
||||
self.fields["postal_code"].initial = location.postal_code
|
||||
|
||||
def clean_latitude(self):
|
||||
latitude = self.cleaned_data.get("latitude")
|
||||
if latitude is not None:
|
||||
try:
|
||||
# Convert to Decimal for precise handling
|
||||
latitude = Decimal(str(latitude))
|
||||
# Round to exactly 6 decimal places
|
||||
latitude = latitude.quantize(Decimal("0.000001"), rounding=ROUND_DOWN)
|
||||
|
||||
# Validate range
|
||||
if latitude < -90 or latitude > 90:
|
||||
raise forms.ValidationError(
|
||||
"Latitude must be between -90 and 90 degrees."
|
||||
)
|
||||
|
||||
# Convert to string to preserve exact decimal places
|
||||
return str(latitude)
|
||||
except (InvalidOperation, TypeError) as e:
|
||||
raise forms.ValidationError("Invalid latitude value.") from e
|
||||
return latitude
|
||||
|
||||
def clean_longitude(self):
|
||||
longitude = self.cleaned_data.get("longitude")
|
||||
if longitude is not None:
|
||||
try:
|
||||
# Convert to Decimal for precise handling
|
||||
longitude = Decimal(str(longitude))
|
||||
# Round to exactly 6 decimal places
|
||||
longitude = longitude.quantize(Decimal("0.000001"), rounding=ROUND_DOWN)
|
||||
|
||||
# Validate range
|
||||
if longitude < -180 or longitude > 180:
|
||||
raise forms.ValidationError(
|
||||
"Longitude must be between -180 and 180 degrees."
|
||||
)
|
||||
|
||||
# Convert to string to preserve exact decimal places
|
||||
return str(longitude)
|
||||
except (InvalidOperation, TypeError) as e:
|
||||
raise forms.ValidationError("Invalid longitude value.") from e
|
||||
return longitude
|
||||
|
||||
def save(self, commit=True):
|
||||
park = super().save(commit=False)
|
||||
|
||||
# Prepare location data
|
||||
location_data = {
|
||||
"name": park.name,
|
||||
"location_type": "park",
|
||||
"latitude": self.cleaned_data.get("latitude"),
|
||||
"longitude": self.cleaned_data.get("longitude"),
|
||||
"street_address": self.cleaned_data.get("street_address"),
|
||||
"city": self.cleaned_data.get("city"),
|
||||
"state": self.cleaned_data.get("state"),
|
||||
"country": self.cleaned_data.get("country"),
|
||||
"postal_code": self.cleaned_data.get("postal_code"),
|
||||
}
|
||||
|
||||
# Handle location: update if exists, create if not
|
||||
try:
|
||||
park_location = park.location
|
||||
# Update existing location
|
||||
for key, value in location_data.items():
|
||||
if key in ["latitude", "longitude"] and value:
|
||||
continue # Handle coordinates separately
|
||||
if hasattr(park_location, key):
|
||||
setattr(park_location, key, value)
|
||||
|
||||
# Handle coordinates if provided
|
||||
if "latitude" in location_data and "longitude" in location_data:
|
||||
if location_data["latitude"] and location_data["longitude"]:
|
||||
park_location.set_coordinates(
|
||||
float(location_data["latitude"]),
|
||||
float(location_data["longitude"]),
|
||||
)
|
||||
park_location.save()
|
||||
except ParkLocation.DoesNotExist:
|
||||
# Create new ParkLocation
|
||||
coordinates_data = {}
|
||||
if "latitude" in location_data and "longitude" in location_data:
|
||||
if location_data["latitude"] and location_data["longitude"]:
|
||||
coordinates_data = {
|
||||
"latitude": float(location_data["latitude"]),
|
||||
"longitude": float(location_data["longitude"]),
|
||||
}
|
||||
|
||||
# Remove coordinate fields from location_data for creation
|
||||
creation_data = {
|
||||
k: v
|
||||
for k, v in location_data.items()
|
||||
if k not in ["latitude", "longitude"]
|
||||
}
|
||||
creation_data.setdefault("country", "USA")
|
||||
|
||||
park_location = ParkLocation.objects.create(park=park, **creation_data)
|
||||
|
||||
if coordinates_data:
|
||||
park_location.set_coordinates(
|
||||
coordinates_data["latitude"], coordinates_data["longitude"]
|
||||
)
|
||||
park_location.save()
|
||||
|
||||
if commit:
|
||||
park.save()
|
||||
|
||||
return park
|
||||
Reference in New Issue
Block a user