From e699ddb62d2b8352629872d0b640982c0bece223 Mon Sep 17 00:00:00 2001 From: Stephan Kerkman Date: Mon, 1 Jun 2026 16:44:05 +0200 Subject: [PATCH] feat: add LocationForm with cover_photo, location_detail view, update location_edit --- plants/forms.py | 6 ++++ plants/templates/plants/location_detail.html | 2 ++ plants/tests/test_location_redesign.py | 35 ++++++++++++++++++++ plants/urls.py | 1 + plants/views/locations.py | 33 ++++++++++++++---- 5 files changed, 70 insertions(+), 7 deletions(-) create mode 100644 plants/templates/plants/location_detail.html diff --git a/plants/forms.py b/plants/forms.py index a27d74d..e0b10f9 100644 --- a/plants/forms.py +++ b/plants/forms.py @@ -71,3 +71,9 @@ class PruningLogForm(forms.ModelForm): 'pruned_on': forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}), 'notes': forms.Textarea(attrs={'rows': 2, 'class': 'form-control'}), } + + +class LocationForm(forms.ModelForm): + class Meta: + model = Location + fields = ['name', 'cover_photo'] diff --git a/plants/templates/plants/location_detail.html b/plants/templates/plants/location_detail.html new file mode 100644 index 0000000..aa29e25 --- /dev/null +++ b/plants/templates/plants/location_detail.html @@ -0,0 +1,2 @@ +{% extends "plants/base.html" %} +{% block content %}{{ location.name }}{% endblock %} diff --git a/plants/tests/test_location_redesign.py b/plants/tests/test_location_redesign.py index 4daa4c4..a543424 100644 --- a/plants/tests/test_location_redesign.py +++ b/plants/tests/test_location_redesign.py @@ -1,6 +1,8 @@ import pytest from django.core.files.uploadedfile import SimpleUploadedFile from plants.models import Location +from django.test import Client +from django.urls import reverse @pytest.mark.django_db @@ -14,3 +16,36 @@ def test_location_cover_photo_field_exists(): loc.save() loc.refresh_from_db() assert loc.cover_photo.name.startswith('locations/') + + +@pytest.fixture +def client(): + return Client() + + +@pytest.mark.django_db +def test_location_detail_view_returns_200(client): + loc = Location.objects.create(name='My Terrace') + resp = client.get(reverse('location_detail', kwargs={'pk': loc.pk})) + assert resp.status_code == 200 + assert b'My Terrace' in resp.content + + +@pytest.mark.django_db +def test_location_edit_with_cover_photo(client): + import io + from PIL import Image + + loc = Location.objects.create(name='Balcony') + buf = io.BytesIO() + Image.new('RGB', (1, 1), color=(0, 255, 0)).save(buf, format='JPEG') + buf.seek(0) + fake_img = SimpleUploadedFile('cover.jpg', buf.read(), content_type='image/jpeg') + resp = client.post( + reverse('location_edit', kwargs={'pk': loc.pk}), + {'name': 'Balcony Updated', 'cover_photo': fake_img}, + ) + assert resp.status_code == 302 + loc.refresh_from_db() + assert loc.name == 'Balcony Updated' + assert loc.cover_photo.name.startswith('locations/') diff --git a/plants/urls.py b/plants/urls.py index 63808cb..3b37fcb 100644 --- a/plants/urls.py +++ b/plants/urls.py @@ -23,6 +23,7 @@ urlpatterns = [ path('photos//delete/', plants.delete_photo, name='delete_photo'), path('pruning/', pruning.pruning_calendar, name='pruning_calendar'), path('locations/', locations.location_list, name='location_list'), + path('locations//', locations.location_detail, name='location_detail'), path('locations//edit/', locations.location_edit, name='location_edit'), path('locations//delete/', locations.location_delete, name='location_delete'), path('species/search/', species.species_search, name='species_search'), diff --git a/plants/views/locations.py b/plants/views/locations.py index e557c37..3bd2401 100644 --- a/plants/views/locations.py +++ b/plants/views/locations.py @@ -2,6 +2,7 @@ from django.shortcuts import render, get_object_or_404, redirect from django.views.decorators.http import require_POST from django.db.models import Count from plants.models import Location, Plant +from plants.forms import LocationForm def location_list(request): @@ -12,18 +13,36 @@ def location_list(request): return redirect('location_list') locations = Location.objects.annotate(plant_count=Count('plants')) - return render(request, 'plants/locations.html', {'locations': locations}) + return render(request, 'plants/locations.html', { + 'locations': locations, + 'active_tab': 'garden', + }) + + +def location_detail(request, pk): + location = get_object_or_404(Location, pk=pk) + plants = location.plants.select_related('species').prefetch_related('photos').all() + return render(request, 'plants/location_detail.html', { + 'location': location, + 'plants': plants, + 'active_tab': 'garden', + }) def location_edit(request, pk): location = get_object_or_404(Location, pk=pk) if request.method == 'POST': - name = request.POST.get('name', '').strip() - if name: - location.name = name - location.save() - return redirect('location_list') - return render(request, 'plants/location_edit.html', {'location': location}) + form = LocationForm(request.POST, request.FILES, instance=location) + if form.is_valid(): + form.save() + return redirect('location_list') + else: + form = LocationForm(instance=location) + return render(request, 'plants/location_edit.html', { + 'location': location, + 'form': form, + 'active_tab': 'garden', + }) @require_POST