86 lines
2.9 KiB
Python
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
|