feat: add LocationForm with cover_photo, location_detail view, update location_edit

This commit is contained in:
Stephan Kerkman 2026-06-01 16:44:05 +02:00
parent 2d7ca494bd
commit e699ddb62d
5 changed files with 70 additions and 7 deletions

View file

@ -71,3 +71,9 @@ class PruningLogForm(forms.ModelForm):
'pruned_on': forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}), 'pruned_on': forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}),
'notes': forms.Textarea(attrs={'rows': 2, 'class': 'form-control'}), 'notes': forms.Textarea(attrs={'rows': 2, 'class': 'form-control'}),
} }
class LocationForm(forms.ModelForm):
class Meta:
model = Location
fields = ['name', 'cover_photo']

View file

@ -0,0 +1,2 @@
{% extends "plants/base.html" %}
{% block content %}{{ location.name }}{% endblock %}

View file

@ -1,6 +1,8 @@
import pytest import pytest
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from plants.models import Location from plants.models import Location
from django.test import Client
from django.urls import reverse
@pytest.mark.django_db @pytest.mark.django_db
@ -14,3 +16,36 @@ def test_location_cover_photo_field_exists():
loc.save() loc.save()
loc.refresh_from_db() loc.refresh_from_db()
assert loc.cover_photo.name.startswith('locations/') 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/')

View file

@ -23,6 +23,7 @@ urlpatterns = [
path('photos/<int:photo_pk>/delete/', plants.delete_photo, name='delete_photo'), path('photos/<int:photo_pk>/delete/', plants.delete_photo, name='delete_photo'),
path('pruning/', pruning.pruning_calendar, name='pruning_calendar'), path('pruning/', pruning.pruning_calendar, name='pruning_calendar'),
path('locations/', locations.location_list, name='location_list'), path('locations/', locations.location_list, name='location_list'),
path('locations/<int:pk>/', locations.location_detail, name='location_detail'),
path('locations/<int:pk>/edit/', locations.location_edit, name='location_edit'), path('locations/<int:pk>/edit/', locations.location_edit, name='location_edit'),
path('locations/<int:pk>/delete/', locations.location_delete, name='location_delete'), path('locations/<int:pk>/delete/', locations.location_delete, name='location_delete'),
path('species/search/', species.species_search, name='species_search'), path('species/search/', species.species_search, name='species_search'),

View file

@ -2,6 +2,7 @@ from django.shortcuts import render, get_object_or_404, redirect
from django.views.decorators.http import require_POST from django.views.decorators.http import require_POST
from django.db.models import Count from django.db.models import Count
from plants.models import Location, Plant from plants.models import Location, Plant
from plants.forms import LocationForm
def location_list(request): def location_list(request):
@ -12,18 +13,36 @@ def location_list(request):
return redirect('location_list') return redirect('location_list')
locations = Location.objects.annotate(plant_count=Count('plants')) 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): def location_edit(request, pk):
location = get_object_or_404(Location, pk=pk) location = get_object_or_404(Location, pk=pk)
if request.method == 'POST': if request.method == 'POST':
name = request.POST.get('name', '').strip() form = LocationForm(request.POST, request.FILES, instance=location)
if name: if form.is_valid():
location.name = name form.save()
location.save() return redirect('location_list')
return redirect('location_list') else:
return render(request, 'plants/location_edit.html', {'location': location}) form = LocationForm(instance=location)
return render(request, 'plants/location_edit.html', {
'location': location,
'form': form,
'active_tab': 'garden',
})
@require_POST @require_POST