From 91182907827046efd7efc5eda629d5f3615e34cb Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 3 Dec 2025 10:55:30 +0000 Subject: [PATCH] New migration strategy: part.0144: - Add new "enabled" field to PartParameterTemplate model - Add new ContentType fields to the "PartParameterTemplate" and "PartParameter" models - Data migration for existing "PartParameter" records part.0145: - Set NOT NULL constraints on new fields - Remove the obsolete "part" field from the "PartParameter" model --- .../migrations/0144_auto_20251203_1045.py | 130 ++++++++++++++++++ .../migrations/0145_auto_20251203_1120.py | 42 ++++++ 2 files changed, 172 insertions(+) create mode 100644 src/backend/InvenTree/part/migrations/0144_auto_20251203_1045.py create mode 100644 src/backend/InvenTree/part/migrations/0145_auto_20251203_1120.py diff --git a/src/backend/InvenTree/part/migrations/0144_auto_20251203_1045.py b/src/backend/InvenTree/part/migrations/0144_auto_20251203_1045.py new file mode 100644 index 0000000000..a178df8f88 --- /dev/null +++ b/src/backend/InvenTree/part/migrations/0144_auto_20251203_1045.py @@ -0,0 +1,130 @@ +# Generated by Django 5.2.8 on 2025-12-03 10:45 + +from django.db import migrations, models +from django.db.models import deletion + + +def update_parameter(apps, schema_editor): + """Data migration to update existing PartParameter records. + + - Set 'model_type' to the ContentType for 'part.Part' + - Set 'model_id' to the existing 'part_id' value + """ + + PartParameter = apps.get_model("part", "PartParameter") + Part = apps.get_model("part", "Part") + ContentType = apps.get_model("contenttypes", "ContentType") + + part_content_type, created = ContentType.objects.get_or_create( + app_label="part", + model="partparameter", + ) + + parmeters_to_update = [] + + for parameter in PartParameter.objects.all(): + parameter.model_type = part_content_type + parameter.model_id = parameter.part_id + parmeters_to_update.append(parameter) + + if len(parmeters_to_update) > 0: + + print(f"Updating {len(parmeters_to_update)} PartParameter records.") + + PartParameter.objects.bulk_update( + parmeters_to_update, + fields=["model_type", "model_id"], + ) + + +def reverse_update_parameter(apps, schema_editor): + """Reverse data migration to restore existing PartParameter records. + + - Set 'part_id' to the existing 'model_id' value + """ + + PartParameter = apps.get_model("part", "PartParameter") + + parmeters_to_update = [] + + for parameter in PartParameter.objects.all(): + parameter.part = parameter.model_id + parmeters_to_update.append(parameter) + + if len(parmeters_to_update) > 0: + + print(f"Reversing update of {len(parmeters_to_update)} PartParameter records.") + + PartParameter.objects.bulk_update( + parmeters_to_update, + fields=["part"], + ) + + +class Migration(migrations.Migration): + """Data migration for making the PartParameterTemplate and PartParameter models generic. + + - Add new fields to the "PartParameterTemplate" and "PartParameter" models + - Migrate existing data to populate the new fields + - Make the new fields non-nullable (if appropriate) + - Remove any obsolete fields (if necessary) + """ + + atomic = False + + dependencies = [ + ("part", "0143_alter_part_image"), + ] + + operations = [ + # Add the new "enabled" field to the PartParameterTemplate model + migrations.AddField( + model_name="partparametertemplate", + name="enabled", + field=models.BooleanField( + default=True, + help_text="Is this parameter template enabled?", + verbose_name="Enabled", + ), + ), + # Add the "model_type" field to the PartParmaeterTemplate model + migrations.AddField( + model_name="partparametertemplate", + name="model_type", + field=models.ForeignKey( + blank=True, null=True, + help_text="Target model type for this parameter template", + on_delete=deletion.SET_NULL, + to="contenttypes.contenttype", + verbose_name="Model type", + ), + ), + # Add the "model_type" field to the PartParameter model + # Note: This field is initially nullable, to allow existing records to remain valid. + migrations.AddField( + model_name="partparameter", + name="model_type", + field=models.ForeignKey( + blank=True, null=True, + on_delete=deletion.CASCADE, + to="contenttypes.contenttype", + ), + ), + # Add the "model_id" field to the PartParameter model + # Note: This field is initially nullable, to allow existing records to remain valid. + migrations.AddField( + model_name="partparameter", + name="model_id", + field=models.PositiveIntegerField( + blank=True, null=True, + help_text="ID of the target model for this parameter", + verbose_name="Model ID", + ) + ), + # Migrate existing PartParameter records to populate the new fields + migrations.RunPython( + update_parameter, + reverse_code=reverse_update_parameter, + ), + + ] diff --git a/src/backend/InvenTree/part/migrations/0145_auto_20251203_1120.py b/src/backend/InvenTree/part/migrations/0145_auto_20251203_1120.py new file mode 100644 index 0000000000..18dc1f6a80 --- /dev/null +++ b/src/backend/InvenTree/part/migrations/0145_auto_20251203_1120.py @@ -0,0 +1,42 @@ +# Generated by Django 5.2.8 on 2025-12-03 11:20 + +from django.db import migrations, models +from django.db.models import deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("part", "0144_auto_20251203_1045"), + ] + + operations = [ + # Update the "model_type" field on the PartParameter model to be non-nullable + migrations.AlterField( + model_name="partparameter", + name="model_type", + field=models.ForeignKey( + on_delete=deletion.CASCADE, + to="contenttypes.contenttype", + ), + ), + # Update the "model_id" field on the PartParameter model to be non-nullable + migrations.AlterField( + model_name="partparameter", + name="model_id", + field=models.PositiveIntegerField( + help_text="ID of the target model for this parameter", + verbose_name="Model ID", + ) + ), + # Remove the "unique_together" constraint on the PartParameter model + migrations.AlterUniqueTogether( + name="partparameter", + unique_together={('model_type', 'model_id', 'template')}, + ), + # Remove the obsolete "part" field from the PartParameter model + migrations.RemoveField( + model_name="partparameter", + name="part", + ), + ]