From b8c4ecd76093a0fe4e22bec23f4e76185f54a6f7 Mon Sep 17 00:00:00 2001
From: Ernesto Rico Schmidt <ernesto@helsinki.at>
Date: Thu, 1 Aug 2024 20:01:47 -0400
Subject: [PATCH] fix: make some foreign keys nullable & on_delete=SET_NULL to
 avoid delete cascade

---
 ...note_image_alter_notelink_type_and_more.py | 173 ++++++++++++++++++
 program/models.py                             |  31 ++--
 2 files changed, 191 insertions(+), 13 deletions(-)
 create mode 100644 program/migrations/0116_alter_note_image_alter_notelink_type_and_more.py

diff --git a/program/migrations/0116_alter_note_image_alter_notelink_type_and_more.py b/program/migrations/0116_alter_note_image_alter_notelink_type_and_more.py
new file mode 100644
index 00000000..efed7fe0
--- /dev/null
+++ b/program/migrations/0116_alter_note_image_alter_notelink_type_and_more.py
@@ -0,0 +1,173 @@
+# Generated by Django 4.2.14 on 2024-08-01 22:26
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("program", "0115_alter_note_options_alter_schedule_options_and_more"),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name="note",
+            name="image",
+            field=models.ForeignKey(
+                null=True,
+                on_delete=django.db.models.deletion.SET_NULL,
+                related_name="notes",
+                to="program.image",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="notelink",
+            name="type",
+            field=models.ForeignKey(
+                default=1,
+                null=True,
+                on_delete=django.db.models.deletion.SET_NULL,
+                to="program.linktype",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="profile",
+            name="image",
+            field=models.ForeignKey(
+                null=True,
+                on_delete=django.db.models.deletion.SET_NULL,
+                related_name="profiles",
+                to="program.image",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="profilelink",
+            name="type",
+            field=models.ForeignKey(
+                default=1,
+                null=True,
+                on_delete=django.db.models.deletion.SET_NULL,
+                to="program.linktype",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="radiosettings",
+            name="fallback_show",
+            field=models.ForeignKey(
+                blank=True,
+                null=True,
+                on_delete=django.db.models.deletion.SET_NULL,
+                related_name="+",
+                to="program.show",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="radiosettings",
+            name="micro_show",
+            field=models.ForeignKey(
+                blank=True,
+                null=True,
+                on_delete=django.db.models.deletion.SET_NULL,
+                related_name="+",
+                to="program.show",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="schedule",
+            name="rrule",
+            field=models.ForeignKey(
+                help_text="A recurrence rule.",
+                null=True,
+                on_delete=django.db.models.deletion.SET_NULL,
+                related_name="schedules",
+                to="program.rrule",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="schedule",
+            name="show",
+            field=models.ForeignKey(
+                help_text="Show the schedule belongs to.",
+                null=True,
+                on_delete=django.db.models.deletion.SET_NULL,
+                related_name="schedules",
+                to="program.show",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="show",
+            name="funding_category",
+            field=models.ForeignKey(
+                blank=True,
+                null=True,
+                on_delete=django.db.models.deletion.SET_NULL,
+                related_name="shows",
+                to="program.fundingcategory",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="show",
+            name="image",
+            field=models.ForeignKey(
+                null=True,
+                on_delete=django.db.models.deletion.SET_NULL,
+                related_name="shows",
+                to="program.image",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="show",
+            name="logo",
+            field=models.ForeignKey(
+                blank=True,
+                null=True,
+                on_delete=django.db.models.deletion.SET_NULL,
+                related_name="logo_shows",
+                to="program.image",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="show",
+            name="predecessor",
+            field=models.ForeignKey(
+                blank=True,
+                null=True,
+                on_delete=django.db.models.deletion.SET_NULL,
+                related_name="successors",
+                to="program.show",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="show",
+            name="type",
+            field=models.ForeignKey(
+                blank=True,
+                null=True,
+                on_delete=django.db.models.deletion.SET_NULL,
+                related_name="shows",
+                to="program.type",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="showlink",
+            name="type",
+            field=models.ForeignKey(
+                default=1,
+                null=True,
+                on_delete=django.db.models.deletion.SET_NULL,
+                to="program.linktype",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="timeslot",
+            name="repetition_of",
+            field=models.ForeignKey(
+                blank=True,
+                null=True,
+                on_delete=django.db.models.deletion.SET_NULL,
+                related_name="repetitions",
+                to="program.timeslot",
+            ),
+        ),
+    ]
diff --git a/program/models.py b/program/models.py
index 17b7ac18..e8e32fcd 100644
--- a/program/models.py
+++ b/program/models.py
@@ -174,7 +174,7 @@ class Profile(models.Model):
     created_at = models.DateTimeField(auto_now_add=True)
     created_by = models.CharField(max_length=150)
     email = models.EmailField(blank=True, help_text="Email address of the profile.")
-    image = models.ForeignKey(Image, null=True, on_delete=models.CASCADE, related_name="profiles")
+    image = models.ForeignKey(Image, null=True, on_delete=models.SET_NULL, related_name="profiles")
     is_active = models.BooleanField(default=True, help_text="True if the profile is active.")
     name = models.CharField(max_length=128, help_text="Display name of the profile.")
     owners = models.ManyToManyField(
@@ -212,7 +212,7 @@ class LinkType(models.Model):
 
 
 class Link(models.Model):
-    type = models.ForeignKey(LinkType, default=1, on_delete=models.CASCADE)
+    type = models.ForeignKey(LinkType, default=1, null=True, on_delete=models.SET_NULL)
     url = models.URLField()
 
     class Meta:
@@ -242,23 +242,23 @@ class Show(models.Model):
     description = models.TextField(blank=True, help_text="Description of this show.")
     email = models.EmailField(blank=True, null=True, help_text="Email address of this show.")
     funding_category = models.ForeignKey(
-        FundingCategory, blank=True, null=True, on_delete=models.CASCADE, related_name="shows"
+        FundingCategory, blank=True, null=True, on_delete=models.SET_NULL, related_name="shows"
     )
     hosts = models.ManyToManyField(Profile, blank=True, related_name="shows")
-    image = models.ForeignKey(Image, null=True, on_delete=models.CASCADE, related_name="shows")
+    image = models.ForeignKey(Image, null=True, on_delete=models.SET_NULL, related_name="shows")
     internal_note = models.TextField(blank=True, help_text="Internal note for this show.")
     is_active = models.BooleanField(default=True, help_text="True if this show is active.")
     is_public = models.BooleanField(default=False, help_text="True if this show is public.")
     language = models.ManyToManyField(Language, blank=True, related_name="shows")
     # TODO: is this really necessary?
     logo = models.ForeignKey(
-        Image, blank=True, null=True, on_delete=models.CASCADE, related_name="logo_shows"
+        Image, blank=True, null=True, on_delete=models.SET_NULL, related_name="logo_shows"
     )
     music_focus = models.ManyToManyField(MusicFocus, blank=True, related_name="shows")
     name = models.CharField(max_length=255, help_text="Name of this Show.")
     owners = models.ManyToManyField(User, blank=True, related_name="shows")
     predecessor = models.ForeignKey(
-        "self", blank=True, null=True, on_delete=models.CASCADE, related_name="successors"
+        "self", blank=True, null=True, on_delete=models.SET_NULL, related_name="successors"
     )
     short_description = models.TextField(help_text="Short description of this show.")
     slug = models.SlugField(
@@ -266,7 +266,7 @@ class Show(models.Model):
     )
     topic = models.ManyToManyField(Topic, blank=True, related_name="shows")
     type = models.ForeignKey(
-        Type, blank=True, null=True, on_delete=models.CASCADE, related_name="shows"
+        Type, blank=True, null=True, on_delete=models.SET_NULL, related_name="shows"
     )
     updated_at = models.DateTimeField(auto_now=True, blank=True, null=True)
     updated_by = models.CharField(blank=True, default="", max_length=150)
@@ -417,12 +417,17 @@ class Schedule(models.Model):
     )
     last_date = models.DateField(help_text="End date of schedule.", null=True)
     rrule = models.ForeignKey(
-        RRule, help_text="A recurrence rule.", on_delete=models.CASCADE, related_name="schedules"
+        RRule,
+        help_text="A recurrence rule.",
+        null=True,
+        on_delete=models.SET_NULL,
+        related_name="schedules",
     )
     show = models.ForeignKey(
         Show,
         help_text="Show the schedule belongs to.",
-        on_delete=models.CASCADE,
+        null=True,
+        on_delete=models.SET_NULL,
         related_name="schedules",
     )
     start_time = models.TimeField(help_text="Start time of schedule.")
@@ -448,7 +453,7 @@ class TimeSlot(models.Model):
     memo = models.TextField(blank=True, help_text="Memo for this timeslot.")
     playlist_id = models.IntegerField(null=True, help_text="Playlist ID of this timeslot.")
     repetition_of = models.ForeignKey(
-        "self", blank=True, null=True, on_delete=models.CASCADE, related_name="repetitions"
+        "self", blank=True, null=True, on_delete=models.SET_NULL, related_name="repetitions"
     )
     schedule = models.ForeignKey(Schedule, on_delete=models.CASCADE, related_name="timeslots")
     start = models.DateTimeField()
@@ -495,7 +500,7 @@ class Note(models.Model):
     )
     created_at = models.DateTimeField(auto_now_add=True)
     created_by = models.CharField(max_length=150)
-    image = models.ForeignKey(Image, null=True, on_delete=models.CASCADE, related_name="notes")
+    image = models.ForeignKey(Image, null=True, on_delete=models.SET_NULL, related_name="notes")
     language = models.ManyToManyField(Language, blank=True, related_name="episodes")
     playlist = models.TextField(blank=True)
     summary = models.TextField(blank=True, help_text="Summary of the Note.")
@@ -650,7 +655,7 @@ class RadioSettings(models.Model):
         blank=True, max_length=32, validators=[validate_fallback_default_pool]
     )
     fallback_show = models.ForeignKey(
-        Show, blank=True, null=True, on_delete=models.CASCADE, related_name="+"
+        Show, blank=True, null=True, on_delete=models.SET_NULL, related_name="+"
     )
     line_in_channels = models.JSONField(
         blank=True,
@@ -659,7 +664,7 @@ class RadioSettings(models.Model):
         validators=[validate_line_in_channels],
     )
     micro_show = models.ForeignKey(
-        Show, blank=True, null=True, on_delete=models.CASCADE, related_name="+"
+        Show, blank=True, null=True, on_delete=models.SET_NULL, related_name="+"
     )
     note_image_aspect_ratio = ImageAspectRadioField(default="16:9")
     note_image_shape = ImageShapeField(default="rect")
-- 
GitLab