Pārlūkot izejas kodu

[scrobbles] Still trying to get migrations passed

Colin Powell 3 nedēļas atpakaļ
vecāks
revīzija
e19d4ed98b

+ 0 - 5
vrobbler/apps/music/migrations/0032_alter_track_albums_alter_track_scrobblableitem_ptr.py

@@ -17,9 +17,4 @@ class Migration(migrations.Migration):
             name='albums',
             field=models.ManyToManyField(blank=True, related_name='tracks', to='music.album'),
         ),
-        migrations.AlterField(
-            model_name='track',
-            name='scrobblableitem_ptr',
-            field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='as_track', serialize=False, to='scrobbles.scrobblableitem'),
-        ),
     ]

+ 46 - 43
vrobbler/apps/scrobbles/migrations/0072_migrate_scrobblablemixin_data.py

@@ -1,4 +1,4 @@
-from django.db import migrations
+from django.db import migrations, models
 from django.apps import apps
 from django.contrib.contenttypes.models import ContentType
 
@@ -8,18 +8,17 @@ def disable_fk_checks(cursor):
 def enable_fk_checks(cursor):
     cursor.execute("PRAGMA foreign_keys = ON;")
 
-
-def copy_tags_raw_sql(cursor, old_table, old_id_column, new_item_id):
-    # Get content_type_id for ScrobblableItem
-    ct_id = ContentType.objects.get(app_label='scrobbles', model='scrobblableitem').id
-
-    # Insert tags directly from old table to ObjectWithGenres
-    cursor.execute(f"""
-        INSERT INTO scrobbles_objectwithgenres (tag_id, content_type_id, object_id)
-        SELECT tag_id, %s, %s
-        FROM old_table_genres
-        WHERE {old_id_column} = %s
-    """, [ct_id, new_item_id, old_item_id])
+M2M_TABLES = {
+    "beers": [("beers_beer_beerstyles", "beer_id")],
+    "music": [("music_track_albums", "track_id")],
+    "boardgames": [
+        ("boardgames_boardgame_publishers", "board_game_id"),
+        ("boardgames_boardgame_designers", "board_game_id"),
+    ],
+    "videogames": [
+        ("videogames_videogame_platforms", "video_game_id")
+    ],
+}
 
 def get_table_columns(cursor, table_name, vendor):
     """
@@ -93,27 +92,44 @@ def create_and_copy_data(apps, schema_editor, lookup_keys, old_to_new_mappings):
             description=data.get("description", "") or "",
             base_run_time_seconds=data.get("base_run_time_seconds"),
         )
-        base.created = data.get("created")
-        base.modified = data.get("modified")
-        base.save(update_fields=["created", "modified"])
+        #base.created = data.get("created")
+        #base.modified = data.get("modified")
+        #base.save(update_fields=["created", "modified"])
 
         mapping[old_id] = base.id
 
         # -----------------------
         # 3. Update media table FK
         # -----------------------
-        cursor.execute(
-            f"UPDATE {table} SET scrobblableitem_ptr_id = %s WHERE id = %s",
-            [base.id, old_id],
-        )
-
-        # -----------------------
-        # 4. Update scrobbles FK
-        # -----------------------
-        cursor.execute(
-            f"UPDATE {scrobble_table} SET item_id = %s WHERE {model_id_field} = %s",
-            [base.id, old_id],
-        )
+        if app_label in M2M_TABLES: 
+            for table_and_key in M2M_TABLES.get(app_label, []):
+                table, key_id = table_and_key
+                print(f"Updating M2M values for {table}: changing {old_id} to {base.id}")
+                cursor.execute(
+                    f"UPDATE {table} SET {key_id} = %s WHERE {key_id} = %s", [base.id, old_id]
+                )
+        else:
+            cursor.execute(
+                f"UPDATE {table} SET scrobblableitem_ptr_id = %s WHERE id = %s",
+                [base.id, old_id],
+            )
+
+            # -----------------------
+            # 4. Update scrobbles FK
+            # -----------------------
+            cursor.execute(
+                f"UPDATE {scrobble_table} SET item_id = %s WHERE {model_id_field} = %s",
+                [base.id, old_id],
+            )
+
+        # M2M
+        #if app_label in M2M_TABLES:
+        #    for table_and_key in M2M_TABLES.get(app_label):
+        #        table, key = table_and_key
+        #        print(f"Updating M2M for {table}")
+        #        cursor.execute(
+        #            f"UPDATE {table} SET {key} = %s WHERE {key} = %s", [base.id, old_id]
+        #        )
 
 
         # -----------------------
@@ -123,11 +139,11 @@ def create_and_copy_data(apps, schema_editor, lookup_keys, old_to_new_mappings):
             ct_id = ContentType.objects.get(app_label=app_label, model=model_name.lower()).id
 
             # Only copy if the old table has tags
-            cursor.execute(f"""
+            cursor.execute("""
                 UPDATE scrobbles_objectwithgenres SET object_id = %s
                 WHERE content_type_id = %s AND object_id = %s
             """, [base.id, ct_id, old_id])
-        except:
+        except Exception:
             print(f"No content type for {app_label}.{model_name}")
 
     print(f"  Finished migrating {app_label}.{model_name}")
@@ -136,7 +152,6 @@ def reassign_foreign_keys(apps, schema_editor, old_to_new_mappings):
     """
     Detect all FKs pointing to old media tables and reassign to new ScrobblableItem.
     """
-    print(old_to_new_mappings)
     db = schema_editor.connection
     cursor = db.cursor()
 
@@ -156,18 +171,6 @@ def reassign_foreign_keys(apps, schema_editor, old_to_new_mappings):
                 for old_id, new_id in mapping.items():
                     cursor.execute(f"UPDATE {table} SET {fk_column} = %s WHERE {fk_column} = %s",
                                    [new_id, old_id])
-
-            # ManyToMany FKs
-            if field.many_to_many and not field.auto_created:
-                through_table = field.remote_field.through._meta.db_table
-                from_col = field.m2m_column_name()
-                to_model = field.remote_field.model
-                if to_model in old_to_new_mappings:
-                    mapping = old_to_new_mappings[to_model]
-                    print(f"Updating M2M {through_table}.{from_col} → ScrobblableItem")
-                    for old_id, new_id in mapping.items():
-                        cursor.execute(f"UPDATE {through_table} SET {field.m2m_reverse_name()} = %s WHERE {field.m2m_reverse_name()} = %s",
-                                       [new_id, old_id])
     enable_fk_checks(cursor)
 
 def backfill_media(apps, schema_editor):

+ 98 - 2
vrobbler/apps/scrobbles/migrations/0074_alter_scrobble_item.py

@@ -1,7 +1,102 @@
 # Generated by Django 4.2.26 on 2025-12-03 19:37
 
 from django.db import migrations, models
-import django.db.models.deletion
+from django.apps import apps
+from django.contrib.contenttypes.models import ContentType
+
+def backfill_media_sqlite(apps, schema_editor):
+    """
+    Backfill media tables into ScrobblableItem for SQLite.
+    Safely updates M2M and FK relationships.
+    """
+    db = schema_editor.connection
+    if db.vendor != "sqlite":
+        raise RuntimeError("This migration is only for SQLite.")
+
+    cursor = db.cursor()
+    models_to_migrate = [
+        ('books', 'Book', 'book_id'),
+        ('books', 'Paper', 'paper_id'),
+        ('music', 'Track', 'track_id'),
+        ('videos', 'Video', 'video_id'),
+        # Add other media tables here...
+    ]
+
+    ScrobblableItem = apps.get_model("scrobbles", "ScrobblableItem")
+
+    for app_label, model_name, old_id_field in models_to_migrate:
+        Model = apps.get_model(app_label, model_name)
+        table = Model._meta.db_table
+
+        print(f"\nMigrating {app_label}.{model_name}...")
+
+        # --- Step 0: Temporarily rename all M2M tables that reference this table ---
+        for m2m in Model._meta.many_to_many:
+            m2m_table = m2m.remote_field.through._meta.db_table
+            backup_table = f"_backup_{m2m_table}"
+            print(f"  Backing up M2M table {m2m_table} -> {backup_table}")
+            cursor.execute(f"ALTER TABLE {m2m_table} RENAME TO {backup_table}")
+
+        # --- Step 1: Select all rows from original table ---
+        columns = [f.name for f in Model._meta.local_fields]
+        select_cols = ", ".join(columns)
+        cursor.execute(f"SELECT {select_cols} FROM {table}")
+        rows = cursor.fetchall()
+        col_index = {name: i for i, name in enumerate(columns)}
+
+        # --- Step 2: Create ScrobblableItem and update FK ---
+        for row in rows:
+            old_id = row[col_index['id']]
+            base = ScrobblableItem.objects.create(
+                uuid=row[col_index.get('uuid')],
+                title=row[col_index.get('title')] or "",
+                description=row[col_index.get('description')] or "",
+                base_run_time_seconds=row[col_index.get('base_run_time_seconds')],
+            )
+
+            # Preserve created/modified if present
+            for fld in ('created', 'modified'):
+                if fld in col_index:
+                    setattr(base, fld, row[col_index[fld]])
+            base.save(update_fields=['created', 'modified'] if 'created' in col_index else None)
+
+            # Update the media table to point to the new PK
+            cursor.execute(
+                f"UPDATE {table} SET scrobblableitem_ptr_id = ? WHERE id = ?",
+                [base.id, old_id]
+            )
+
+            # Update scrobbles table FK
+            scrobble_table = "scrobbles_scrobble"
+            cursor.execute(
+                f"UPDATE {scrobble_table} SET item_id = ? WHERE {old_id_field} = ?",
+                [base.id, old_id]
+            )
+
+        # --- Step 3: Restore M2M tables pointing to new IDs ---
+        for m2m in Model._meta.many_to_many:
+            old_m2m_table = f"_backup_{m2m.remote_field.through._meta.db_table}"
+            new_m2m_table = m2m.remote_field.through._meta.db_table
+            print(f"  Restoring M2M table {new_m2m_table} from {old_m2m_table}")
+
+            # Recreate the M2M table
+            columns = [f.name for f in m2m.remote_field.through._meta.local_fields]
+            cols_str = ", ".join(columns)
+            cursor.execute(f"INSERT INTO {new_m2m_table} ({cols_str}) "
+                           f"SELECT {cols_str} FROM {old_m2m_table} backup "
+                           f"JOIN {table} media ON backup.{old_id_field} = media.id")
+
+            # Drop backup
+            cursor.execute(f"DROP TABLE {old_m2m_table}")
+
+        print(f"Finished migrating {app_label}.{model_name}")
+
+def backfill_media(apps, schema_editor):
+    if schema_editor.connection.vendor == "sqlite":
+        backfill_media_sqlite(apps, schema_editor)
+    else:
+        # Postgres-safe logic can go here
+        pass
 
 
 class Migration(migrations.Migration):
@@ -11,9 +106,10 @@ class Migration(migrations.Migration):
     ]
 
     operations = [
+        migrations.RunPython(backfill_media, reverse_code=migrations.RunPython.noop),
         migrations.AlterField(
             model_name='scrobble',
             name='item',
-            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='scrobble_set', to='scrobbles.scrobblableitem'),
+            field=models.ForeignKey(null=True, on_delete=models.deletion.CASCADE, related_name='scrobble_set', to='scrobbles.scrobblableitem'),
         ),
     ]