bloombase/plants/migrations/0004_location_plantphoto.py
Stephan Kerkman b88ba03884 feat: location FK, multi-photo gallery, hide pruning history
- Location model: plants now FK to Location instead of free-text CharField
- Data migration 0004 converts existing location strings to Location objects
- PlantForm uses ModelChoiceField dropdown + inline "add new location" text field
- PlantPhoto model: multiple photos per plant with is_thumbnail flag
- Plant.thumbnail_url property: picks marked thumbnail, falls back to first photo then legacy photo field
- Photo gallery partial: upload, set thumbnail (★), delete (✕) with HTMX swaps on #photo-gallery
- plant_detail: uses thumbnail_url for hero image, pruning history section removed
- plant_list search updated: location__name__icontains instead of location__icontains
- Three new URL routes: upload_photo, set_thumbnail, delete_photo
- All 53 tests updated and passing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 18:27:45 +02:00

71 lines
2.4 KiB
Python

import django.db.models.deletion
from django.db import migrations, models
def migrate_location_strings(apps, schema_editor):
Plant = apps.get_model('plants', 'Plant')
Location = apps.get_model('plants', 'Location')
for plant in Plant.objects.all():
name = (plant.location_old or '').strip()
if name:
loc, _ = Location.objects.get_or_create(name=name)
plant.location_new = loc
plant.save(update_fields=['location_new'])
class Migration(migrations.Migration):
dependencies = [
('plants', '0003_add_bloom_months'),
]
operations = [
migrations.CreateModel(
name='Location',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, unique=True)),
],
options={
'ordering': ['name'],
},
),
migrations.CreateModel(
name='PlantPhoto',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('image', models.ImageField(upload_to='plants/photos/')),
('is_thumbnail', models.BooleanField(default=False)),
('uploaded_at', models.DateTimeField(auto_now_add=True)),
('plant', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='photos', to='plants.plant')),
],
options={
'ordering': ['uploaded_at'],
},
),
migrations.RenameField(
model_name='plant',
old_name='location',
new_name='location_old',
),
migrations.AddField(
model_name='plant',
name='location_new',
field=models.ForeignKey(
blank=True, null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name='plants',
to='plants.location',
),
),
migrations.RunPython(migrate_location_strings, migrations.RunPython.noop),
migrations.RemoveField(
model_name='plant',
name='location_old',
),
migrations.RenameField(
model_name='plant',
old_name='location_new',
new_name='location',
),
]