- 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>
71 lines
2.4 KiB
Python
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',
|
|
),
|
|
]
|