|
| 1 | +""" |
| 2 | +Data migration: fix Object file paths in the database. |
| 3 | +
|
| 4 | +Migration 0031 moved files to objects/<pk>/ folders but failed to persist |
| 5 | +the new paths in the DB. |
| 6 | +This migration updates the DB to match the actual file locations. |
| 7 | +""" |
| 8 | + |
| 9 | +import logging |
| 10 | + |
| 11 | +from django.db import migrations |
| 12 | + |
| 13 | +logger = logging.getLogger(__name__) |
| 14 | + |
| 15 | + |
| 16 | +def fix_object_file_paths(apps, schema_editor): |
| 17 | + """Update DB paths to match the objects/<pk>/ folder structure.""" |
| 18 | + Object = apps.get_model("core", "Object") |
| 19 | + |
| 20 | + for obj in Object.objects.only( |
| 21 | + "pk", "source", "audio_description", "thumbnail", |
| 22 | + "spritesheet_file", "spritesheet_metadata", |
| 23 | + ).iterator(): |
| 24 | + if not obj.source: |
| 25 | + continue |
| 26 | + |
| 27 | + storage = obj.source.storage |
| 28 | + updates = {} |
| 29 | + |
| 30 | + # Source file |
| 31 | + ext = ( |
| 32 | + obj.source.name.rsplit(".", 1)[-1].lower() |
| 33 | + if "." in obj.source.name |
| 34 | + else "" |
| 35 | + ) |
| 36 | + expected_source = f"objects/{obj.pk}/source.{ext}" |
| 37 | + if obj.source.name != expected_source: |
| 38 | + if storage.exists(expected_source): |
| 39 | + updates["source"] = expected_source |
| 40 | + |
| 41 | + # Audio description |
| 42 | + if obj.audio_description: |
| 43 | + ad_ext = ( |
| 44 | + obj.audio_description.name.rsplit(".", 1)[-1].lower() |
| 45 | + if "." in obj.audio_description.name |
| 46 | + else "" |
| 47 | + ) |
| 48 | + expected_ad = f"objects/{obj.pk}/audio_description.{ad_ext}" |
| 49 | + if obj.audio_description.name != expected_ad: |
| 50 | + if storage.exists(expected_ad): |
| 51 | + updates["audio_description"] = expected_ad |
| 52 | + |
| 53 | + # Thumbnail |
| 54 | + if obj.thumbnail: |
| 55 | + expected_thumb = f"objects/{obj.pk}/thumbnail.png" |
| 56 | + if obj.thumbnail.name != expected_thumb: |
| 57 | + if storage.exists(expected_thumb): |
| 58 | + updates["thumbnail"] = expected_thumb |
| 59 | + |
| 60 | + # Spritesheet |
| 61 | + if obj.spritesheet_file: |
| 62 | + expected_ss = f"objects/{obj.pk}/spritesheet.png" |
| 63 | + if obj.spritesheet_file.name != expected_ss: |
| 64 | + if storage.exists(expected_ss): |
| 65 | + updates["spritesheet_file"] = expected_ss |
| 66 | + |
| 67 | + # Spritesheet metadata |
| 68 | + if obj.spritesheet_metadata: |
| 69 | + expected_meta = f"objects/{obj.pk}/metadata.json" |
| 70 | + if obj.spritesheet_metadata.name != expected_meta: |
| 71 | + if storage.exists(expected_meta): |
| 72 | + updates["spritesheet_metadata"] = expected_meta |
| 73 | + |
| 74 | + if updates: |
| 75 | + Object.objects.filter(pk=obj.pk).update(**updates) |
| 76 | + logger.info("Object %s: paths fixed: %s", obj.pk, list(updates.keys())) |
| 77 | + |
| 78 | + |
| 79 | +class Migration(migrations.Migration): |
| 80 | + |
| 81 | + dependencies = [ |
| 82 | + ("core", "0037_remove_object_insert_insert_and_more"), |
| 83 | + ] |
| 84 | + |
| 85 | + operations = [ |
| 86 | + migrations.RunPython( |
| 87 | + fix_object_file_paths, |
| 88 | + reverse_code=migrations.RunPython.noop, |
| 89 | + ), |
| 90 | + ] |
0 commit comments