Files
vrobbler/vrobbler/apps/birds/forms.py

86 lines
2.9 KiB
Python

import json
from birds.models import Bird, BirdSightingEntry
from django import forms
class BirdSightingsWidget(forms.Widget):
template_name = "birds/bird_sightings_widget.html"
class Media:
js = ("birds/bird_sightings.js",)
def value_from_datadict(self, data, files, name):
bird_ids = data.getlist(f"{name}_bird_id")
quantities = data.getlist(f"{name}_quantity")
notes = data.getlist(f"{name}_sighting_notes")
return {
"bird_id": bird_ids,
"quantity": quantities,
"sighting_notes": notes,
}
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
sightings = []
if value:
if isinstance(value, str):
try:
value = json.loads(value)
except (json.JSONDecodeError, TypeError):
value = []
for item in (value or []):
if isinstance(item, dict):
sightings.append(item)
elif isinstance(item, BirdSightingEntry):
sightings.append(item.asdict)
context["widget"]["sightings"] = sightings
context["widget"]["birds"] = Bird.objects.all().order_by("common_name")
return context
class BirdSightingsField(forms.Field):
widget = BirdSightingsWidget
def clean(self, value):
if not value:
return None
result = []
bird_ids = value.get("bird_id", []) if isinstance(value, dict) else []
quantities = value.get("quantity", []) if isinstance(value, dict) else []
notes_list = (
value.get("sighting_notes", []) if isinstance(value, dict) else []
)
if isinstance(bird_ids, list):
for i, bird_id in enumerate(bird_ids):
if not bird_id:
continue
try:
bird_id = int(bird_id)
quantity = int(quantities[i]) if i < len(quantities) else 1
except (ValueError, TypeError, IndexError):
continue
note = notes_list[i] if i < len(notes_list) else ""
entry = BirdSightingEntry(
bird_id=bird_id,
quantity=quantity,
sighting_notes=note or None,
)
result.append(entry.asdict)
elif bird_ids:
try:
bird_id = int(bird_ids)
quantity = int(quantities) if quantities else 1
except (ValueError, TypeError):
raise forms.ValidationError("Invalid bird sighting data")
note = notes_list if notes_list else ""
entry = BirdSightingEntry(
bird_id=bird_id,
quantity=quantity,
sighting_notes=note or None,
)
result.append(entry.asdict)
return result if result else None