mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 14:31:08 -05:00
okay fine
This commit is contained in:
311
parks/forms.py
311
parks/forms.py
@@ -1,191 +1,148 @@
|
||||
from django import forms
|
||||
from django.urls import reverse_lazy
|
||||
from decimal import Decimal, InvalidOperation, ROUND_DOWN
|
||||
from .models import Park
|
||||
from cities_light.models import Country, Region, City
|
||||
|
||||
class LocationFilterForm(forms.Form):
|
||||
country = forms.CharField(
|
||||
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': 'Select or type a country',
|
||||
'hx-get': reverse_lazy('parks:country_search'),
|
||||
'hx-trigger': 'focus, input',
|
||||
'hx-target': '#country-results',
|
||||
'autocomplete': 'off'
|
||||
})
|
||||
)
|
||||
region = forms.CharField(
|
||||
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': 'Select or type a region/state',
|
||||
'hx-get': reverse_lazy('parks:region_search'),
|
||||
'hx-trigger': 'focus, input',
|
||||
'hx-target': '#region-results',
|
||||
'autocomplete': 'off'
|
||||
})
|
||||
)
|
||||
city = forms.CharField(
|
||||
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': 'Select or type a city',
|
||||
'hx-get': reverse_lazy('parks:city_search'),
|
||||
'hx-trigger': 'focus, input',
|
||||
'hx-target': '#city-results',
|
||||
'autocomplete': 'off'
|
||||
})
|
||||
)
|
||||
|
||||
class ParkForm(forms.ModelForm):
|
||||
# Hidden fields for actual model relations
|
||||
country = forms.ModelChoiceField(queryset=Country.objects.all(), required=True, widget=forms.HiddenInput())
|
||||
region = forms.ModelChoiceField(queryset=Region.objects.all(), required=False, widget=forms.HiddenInput())
|
||||
city = forms.ModelChoiceField(queryset=City.objects.all(), required=False, widget=forms.HiddenInput())
|
||||
|
||||
# Visible fields for Alpine.js
|
||||
country_name = forms.CharField(
|
||||
label="Country",
|
||||
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': 'Select or type a country',
|
||||
'data-autocomplete': 'true'
|
||||
})
|
||||
)
|
||||
|
||||
region_name = forms.CharField(
|
||||
label="Region/State",
|
||||
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': 'Select or type a region/state',
|
||||
'data-autocomplete': 'true'
|
||||
})
|
||||
)
|
||||
|
||||
city_name = forms.CharField(
|
||||
label="City",
|
||||
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': 'Select or type a city',
|
||||
'data-autocomplete': 'true'
|
||||
})
|
||||
)
|
||||
"""Form for creating and updating Park objects with location support"""
|
||||
|
||||
class Meta:
|
||||
model = Park
|
||||
fields = ['name', 'description', 'owner', 'status', 'opening_date', 'closing_date',
|
||||
'operating_season', 'size_acres', 'website', 'country', 'region', 'city']
|
||||
fields = [
|
||||
"name",
|
||||
"description",
|
||||
"owner",
|
||||
"status",
|
||||
"opening_date",
|
||||
"closing_date",
|
||||
"operating_season",
|
||||
"size_acres",
|
||||
"website",
|
||||
"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
|
||||
}),
|
||||
'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'
|
||||
}),
|
||||
"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,
|
||||
}
|
||||
),
|
||||
"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",
|
||||
}
|
||||
),
|
||||
# Location fields
|
||||
"latitude": forms.HiddenInput(),
|
||||
"longitude": forms.HiddenInput(),
|
||||
"street_address": 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.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.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.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.TextInput(
|
||||
attrs={
|
||||
"class": "w-full border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
||||
}
|
||||
),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
instance = kwargs.get('instance')
|
||||
if instance:
|
||||
def clean_latitude(self):
|
||||
latitude = self.cleaned_data.get('latitude')
|
||||
if latitude is not None:
|
||||
try:
|
||||
if instance.country:
|
||||
self.fields['country_name'].initial = instance.country.name
|
||||
self.fields['country'].initial = instance.country
|
||||
except Country.DoesNotExist:
|
||||
pass
|
||||
# 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):
|
||||
raise forms.ValidationError("Invalid latitude value.")
|
||||
return latitude
|
||||
|
||||
def clean_longitude(self):
|
||||
longitude = self.cleaned_data.get('longitude')
|
||||
if longitude is not None:
|
||||
try:
|
||||
if instance.region:
|
||||
self.fields['region_name'].initial = instance.region.name
|
||||
self.fields['region'].initial = instance.region
|
||||
except Region.DoesNotExist:
|
||||
pass
|
||||
|
||||
try:
|
||||
if instance.city:
|
||||
self.fields['city_name'].initial = instance.city.name
|
||||
self.fields['city'].initial = instance.city
|
||||
except City.DoesNotExist:
|
||||
pass
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
country_name = cleaned_data.get('country_name')
|
||||
region_name = cleaned_data.get('region_name')
|
||||
city_name = cleaned_data.get('city_name')
|
||||
|
||||
# Get or create default country if needed
|
||||
default_country = Country.objects.first()
|
||||
if not default_country:
|
||||
default_country = Country.objects.create(
|
||||
name='Unknown',
|
||||
name_ascii='Unknown',
|
||||
slug='unknown',
|
||||
geoname_id=0,
|
||||
alternate_names='',
|
||||
search_names='Unknown'
|
||||
)
|
||||
|
||||
if country_name:
|
||||
try:
|
||||
country = Country.objects.get(name__iexact=country_name)
|
||||
cleaned_data['country'] = country
|
||||
except Country.DoesNotExist:
|
||||
cleaned_data['country'] = default_country
|
||||
else:
|
||||
raise forms.ValidationError("Country is required")
|
||||
|
||||
if region_name and cleaned_data.get('country'):
|
||||
try:
|
||||
region = Region.objects.get(
|
||||
name__iexact=region_name,
|
||||
country=cleaned_data['country']
|
||||
)
|
||||
cleaned_data['region'] = region
|
||||
except Region.DoesNotExist:
|
||||
cleaned_data['region'] = None
|
||||
|
||||
if city_name and cleaned_data.get('region'):
|
||||
try:
|
||||
city = City.objects.get(
|
||||
name__iexact=city_name,
|
||||
region=cleaned_data['region']
|
||||
)
|
||||
cleaned_data['city'] = city
|
||||
except City.DoesNotExist:
|
||||
cleaned_data['city'] = None
|
||||
|
||||
return cleaned_data
|
||||
# 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):
|
||||
raise forms.ValidationError("Invalid longitude value.")
|
||||
return longitude
|
||||
|
||||
Reference in New Issue
Block a user