feat: add plant form with species pre-fill
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
88976bb351
commit
3bc27a345f
3 changed files with 129 additions and 1 deletions
70
plants/templates/plants/plant_form.html
Normal file
70
plants/templates/plants/plant_form.html
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
{% extends "plants/base.html" %}
|
||||||
|
{% block title %}{{ title }} — PlantDB{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="d-flex align-items-center gap-2 mb-3">
|
||||||
|
<a href="{% url 'plant_list' %}" class="btn btn-sm btn-outline-secondary">←</a>
|
||||||
|
<h5 class="mb-0">{{ title }}</h5>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if species %}
|
||||||
|
<div class="alert alert-success py-2 d-flex align-items-center gap-2">
|
||||||
|
{% if species.api_image_url %}
|
||||||
|
<img src="{{ species.api_image_url }}" width="36" height="36" class="rounded" style="object-fit:cover;">
|
||||||
|
{% endif %}
|
||||||
|
<div>
|
||||||
|
<strong>{{ species.common_name }}</strong>
|
||||||
|
{% if species.scientific_name %}<small class="text-muted fst-italic ms-1">{{ species.scientific_name }}</small>{% endif %}
|
||||||
|
<div class="small">Care info pre-filled from species database.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form method="POST" enctype="multipart/form-data">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="species_id" value="{{ species_id|default:'' }}">
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-semibold">Your name for this plant *</label>
|
||||||
|
{{ form.name }}
|
||||||
|
{% if form.name.errors %}<div class="text-danger small">{{ form.name.errors }}</div>{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-semibold">Location</label>
|
||||||
|
{{ form.location }}
|
||||||
|
{% if form.location.errors %}<div class="text-danger small">{{ form.location.errors }}</div>{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3 form-check">
|
||||||
|
{{ form.is_indoor }}
|
||||||
|
<label class="form-check-label" for="{{ form.is_indoor.id_for_label }}">Indoor plant</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-semibold">Pruning months</label>
|
||||||
|
<div class="month-checkboxes d-flex flex-wrap gap-1">
|
||||||
|
{% for widget in form.pruning_months %}
|
||||||
|
<div class="form-check form-check-inline border rounded px-2 py-1">
|
||||||
|
{{ widget.tag }}
|
||||||
|
<label class="form-check-label" for="{{ widget.id_for_label }}">{{ widget.choice_label }}</label>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-semibold">Photo</label>
|
||||||
|
{{ form.photo }}
|
||||||
|
<div class="form-text">Take a photo with your camera or upload one.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-semibold">Notes</label>
|
||||||
|
{{ form.notes }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-grid">
|
||||||
|
<button type="submit" class="btn btn-success">Save plant</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -149,3 +149,36 @@ class TestSpeciesSearch:
|
||||||
'api_image_url': '',
|
'api_image_url': '',
|
||||||
})
|
})
|
||||||
assert Species.objects.filter(perenual_id=99).count() == 1
|
assert Species.objects.filter(perenual_id=99).count() == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
class TestPlantAdd:
|
||||||
|
def test_add_with_species_shows_prefilled_form(self, client):
|
||||||
|
from plants.models import Species
|
||||||
|
s = Species.objects.create(common_name='Rose', pruning_months=[2, 8])
|
||||||
|
resp = client.get(reverse('plant_add') + f'?species_id={s.pk}')
|
||||||
|
assert resp.status_code == 200
|
||||||
|
assert resp.context['species'] == s
|
||||||
|
|
||||||
|
def test_post_creates_plant_and_redirects(self, client):
|
||||||
|
resp = client.post(reverse('plant_add'), {
|
||||||
|
'name': 'New Fern',
|
||||||
|
'location': 'Office',
|
||||||
|
'is_indoor': 'on',
|
||||||
|
'notes': '',
|
||||||
|
'pruning_months': [],
|
||||||
|
})
|
||||||
|
assert Plant.objects.filter(name='New Fern').exists()
|
||||||
|
plant = Plant.objects.get(name='New Fern')
|
||||||
|
assert resp.status_code == 302
|
||||||
|
assert resp['Location'] == f'/plants/{plant.pk}/'
|
||||||
|
|
||||||
|
def test_post_with_species_links_species(self, client):
|
||||||
|
from plants.models import Species
|
||||||
|
s = Species.objects.create(common_name='Rose', pruning_months=[2])
|
||||||
|
client.post(
|
||||||
|
reverse('plant_add') + f'?species_id={s.pk}',
|
||||||
|
{'name': 'Garden Rose', 'location': 'Garden', 'notes': '', 'pruning_months': ['2']},
|
||||||
|
)
|
||||||
|
plant = Plant.objects.get(name='Garden Rose')
|
||||||
|
assert plant.species == s
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,32 @@ def log_pruning(request, pk):
|
||||||
|
|
||||||
|
|
||||||
def plant_add(request):
|
def plant_add(request):
|
||||||
return render(request, 'plants/species_search.html')
|
species_id = request.GET.get('species_id') or request.POST.get('species_id')
|
||||||
|
species = None
|
||||||
|
|
||||||
|
if species_id and species_id != 'skip':
|
||||||
|
species = get_object_or_404(Species, pk=species_id)
|
||||||
|
|
||||||
|
if species_id is None and request.method == 'GET':
|
||||||
|
return render(request, 'plants/species_search.html')
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = PlantForm(request.POST, request.FILES)
|
||||||
|
if form.is_valid():
|
||||||
|
plant = form.save(commit=False)
|
||||||
|
plant.species = species
|
||||||
|
plant.save()
|
||||||
|
return redirect('plant_detail', pk=plant.pk)
|
||||||
|
else:
|
||||||
|
initial = {'pruning_months': species.pruning_months} if species else {}
|
||||||
|
form = PlantForm(initial=initial)
|
||||||
|
|
||||||
|
return render(request, 'plants/plant_form.html', {
|
||||||
|
'form': form,
|
||||||
|
'species': species,
|
||||||
|
'species_id': species_id,
|
||||||
|
'title': 'Add Plant',
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
def plant_edit(request, pk):
|
def plant_edit(request, pk):
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue