diff --git a/src/app/forms.py b/src/app/forms.py index 77ef4f06..5b95525b 100644 --- a/src/app/forms.py +++ b/src/app/forms.py @@ -184,7 +184,7 @@ class ManualItemForm(forms.ModelForm): instance.media_id = parent_season.item.media_id instance.season_number = parent_season.item.season_number else: - instance.media_id = Item.generate_manual_id(instance.media_type) + instance.media_id = Item.generate_manual_id() if commit: instance.save() diff --git a/src/app/migrations/0056_alter_item_media_id.py b/src/app/migrations/0056_alter_item_media_id.py new file mode 100644 index 00000000..b144f676 --- /dev/null +++ b/src/app/migrations/0056_alter_item_media_id.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.12 on 2026-04-02 14:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('app', '0055_alter_item_media_type'), + ] + + operations = [ + migrations.AlterField( + model_name='item', + name='media_id', + field=models.CharField(max_length=36), + ), + ] diff --git a/src/app/models.py b/src/app/models.py index 444e228e..51232618 100644 --- a/src/app/models.py +++ b/src/app/models.py @@ -1,4 +1,5 @@ import logging +import uuid from django.apps import apps from django.conf import settings @@ -12,14 +13,13 @@ from django.db.models import ( CheckConstraint, Count, F, - IntegerField, Max, Prefetch, Q, UniqueConstraint, Window, ) -from django.db.models.functions import Cast, RowNumber +from django.db.models.functions import RowNumber from django.utils import timezone from model_utils import FieldTracker from model_utils.fields import MonitorField @@ -67,7 +67,8 @@ class MediaTypes(models.TextChoices): class Item(CalendarTriggerMixin, models.Model): """Model to store basic information about media items.""" - media_id = models.CharField(max_length=20) + # limited by uuid for manual entries + media_id = models.CharField(max_length=36) source = models.CharField( max_length=20, choices=Sources, @@ -168,21 +169,12 @@ class Item(CalendarTriggerMixin, models.Model): return name @classmethod - def generate_manual_id(cls, media_type): - """Generate a new ID for manual items.""" - latest_item = ( - cls.objects.filter(source=Sources.MANUAL.value, media_type=media_type) - .annotate( - media_id_int=Cast("media_id", IntegerField()), - ) - .order_by("-media_id_int") - .first() - ) + def generate_manual_id(cls): + """Generate a new ID for manual items. - if latest_item is None: - return "1" - - return str(int(latest_item.media_id) + 1) + Uses a UUID to ensure uniqueness. + """ + return str(uuid.uuid4()) def fetch_releases(self, delay): """Fetch releases for the item.""" diff --git a/src/app/tests/test_forms.py b/src/app/tests/test_forms.py index a281a7f8..df707d6b 100644 --- a/src/app/tests/test_forms.py +++ b/src/app/tests/test_forms.py @@ -279,7 +279,7 @@ class ManualItemFormTest(TestCase): # Save and verify item = form.save() self.assertEqual(item.source, Sources.MANUAL.value) - self.assertEqual(item.media_id, "1") + self.assertTrue(item.media_id) self.assertIsNone(item.season_number) self.assertIsNone(item.episode_number) @@ -377,7 +377,7 @@ class ManualItemFormTest(TestCase): self.assertTrue(form2.is_valid()) item2 = form2.save() - # IDs should be different but follow the pattern + # IDs should be different self.assertNotEqual(item1.media_id, item2.media_id) - self.assertEqual(item1.media_id, "1") - self.assertEqual(item2.media_id, "2") + self.assertTrue(item1.media_id) + self.assertTrue(item2.media_id)