From 168c87797079b642df00e9e111aaff870137ef11 Mon Sep 17 00:00:00 2001 From: Disco DeDisco Date: Sun, 22 Feb 2026 23:56:29 -0500 Subject: [PATCH] refactored lists to have more descriptive urlpatterns; cascading changes across API, dashboard app & even FTs; restarted staging server db w. new migrations --- src/apps/api/tests/integrated/test_views.py | 4 +-- src/apps/api/urls.py | 4 +-- src/apps/dashboard/migrations/0001_initial.py | 9 ++++-- .../dashboard/migrations/0002_list_owner.py | 21 -------------- .../migrations/0003_list_shared_with.py | 20 ------------- src/apps/dashboard/models.py | 4 +++ .../dashboard/tests/integrated/test_models.py | 2 +- .../dashboard/tests/integrated/test_views.py | 28 +++++++++---------- src/apps/dashboard/urls.py | 4 +-- .../test_simple_list_creation.py | 10 +++++-- 10 files changed, 40 insertions(+), 66 deletions(-) delete mode 100644 src/apps/dashboard/migrations/0002_list_owner.py delete mode 100644 src/apps/dashboard/migrations/0003_list_shared_with.py diff --git a/src/apps/api/tests/integrated/test_views.py b/src/apps/api/tests/integrated/test_views.py index 96b1ee9..3dd5be7 100644 --- a/src/apps/api/tests/integrated/test_views.py +++ b/src/apps/api/tests/integrated/test_views.py @@ -20,7 +20,7 @@ class ListDetailAPITest(BaseAPITest): response = self.client.get(f"/api/lists/{list_.id}/") self.assertEqual(response.status_code, 200) - self.assertEqual(response.data["id"], list_.id) + self.assertEqual(response.data["id"], str(list_.id)) self.assertEqual(len(response.data["items"]), 2) class ListItemsAPITest(BaseAPITest): @@ -48,7 +48,7 @@ class ListsAPITest(BaseAPITest): self.assertEqual(response.status_code, 200) self.assertEqual(len(response.data), 1) - self.assertEqual(response.data[0]["id"], list1.id) + self.assertEqual(response.data[0]["id"], str(list1.id)) def test_post_creates_list_with_item(self): response = self.client.post( diff --git a/src/apps/api/urls.py b/src/apps/api/urls.py index 4baac40..08e6d5f 100644 --- a/src/apps/api/urls.py +++ b/src/apps/api/urls.py @@ -5,7 +5,7 @@ from . import views urlpatterns = [ path('', views.ListsAPI.as_view(), name='api_lists'), - path('/', views.ListDetailAPI.as_view(), name='api_list_detail'), - path('/items/', views.ListItemsAPI.as_view(), name='api_list_items'), + path('/', views.ListDetailAPI.as_view(), name='api_list_detail'), + path('/items/', views.ListItemsAPI.as_view(), name='api_list_items'), ] diff --git a/src/apps/dashboard/migrations/0001_initial.py b/src/apps/dashboard/migrations/0001_initial.py index e9e6dd7..c5c3fd6 100644 --- a/src/apps/dashboard/migrations/0001_initial.py +++ b/src/apps/dashboard/migrations/0001_initial.py @@ -1,6 +1,8 @@ -# Generated by Django 6.0 on 2026-02-08 01:19 +# Generated by Django 6.0 on 2026-02-23 04:30 import django.db.models.deletion +import uuid +from django.conf import settings from django.db import migrations, models @@ -9,13 +11,16 @@ class Migration(migrations.Migration): initial = True dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name='List', fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('owner', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='lists', to=settings.AUTH_USER_MODEL)), + ('shared_with', models.ManyToManyField(blank=True, related_name='shared_lists', to=settings.AUTH_USER_MODEL)), ], ), migrations.CreateModel( diff --git a/src/apps/dashboard/migrations/0002_list_owner.py b/src/apps/dashboard/migrations/0002_list_owner.py deleted file mode 100644 index df4b4cc..0000000 --- a/src/apps/dashboard/migrations/0002_list_owner.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 6.0 on 2026-02-09 03:29 - -import django.db.models.deletion -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('dashboard', '0001_initial'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.AddField( - model_name='list', - name='owner', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='lists', to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/src/apps/dashboard/migrations/0003_list_shared_with.py b/src/apps/dashboard/migrations/0003_list_shared_with.py deleted file mode 100644 index 38f740f..0000000 --- a/src/apps/dashboard/migrations/0003_list_shared_with.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 6.0 on 2026-02-18 18:13 - -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('dashboard', '0002_list_owner'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.AddField( - model_name='list', - name='shared_with', - field=models.ManyToManyField(blank=True, related_name='shared_lists', to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/src/apps/dashboard/models.py b/src/apps/dashboard/models.py index 347b40f..bb1b7f0 100644 --- a/src/apps/dashboard/models.py +++ b/src/apps/dashboard/models.py @@ -1,7 +1,11 @@ +import uuid + from django.db import models from django.urls import reverse + class List(models.Model): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) owner = models.ForeignKey( "lyric.User", related_name="lists", diff --git a/src/apps/dashboard/tests/integrated/test_models.py b/src/apps/dashboard/tests/integrated/test_models.py index 25fd2c7..4564c88 100644 --- a/src/apps/dashboard/tests/integrated/test_models.py +++ b/src/apps/dashboard/tests/integrated/test_models.py @@ -43,7 +43,7 @@ class ItemModelTest(TestCase): class ListModelTest(TestCase): def test_get_absolute_url(self): mylist = List.objects.create() - self.assertEqual(mylist.get_absolute_url(), f"/dashboard/{mylist.id}/") + self.assertEqual(mylist.get_absolute_url(), f"/dashboard/list/{mylist.id}/") def test_list_items_order(self): list1 = List.objects.create() diff --git a/src/apps/dashboard/tests/integrated/test_views.py b/src/apps/dashboard/tests/integrated/test_views.py index c860020..796e5e6 100644 --- a/src/apps/dashboard/tests/integrated/test_views.py +++ b/src/apps/dashboard/tests/integrated/test_views.py @@ -36,7 +36,7 @@ class NewListTest(TestCase): def test_redirects_after_POST(self): response = self.client.post("/dashboard/new_list", data={"text": "A new list item"}) new_list = List.objects.get() - self.assertRedirects(response, f"/dashboard/{new_list.id}/") + self.assertRedirects(response, f"/dashboard/list/{new_list.id}/") # Post invalid input helper def post_invalid_input(self): @@ -58,12 +58,12 @@ class NewListTest(TestCase): class ListViewTest(TestCase): def test_uses_list_template(self): mylist = List.objects.create() - response = self.client.get(f"/dashboard/{mylist.id}/") + response = self.client.get(f"/dashboard/list/{mylist.id}/") self.assertTemplateUsed(response, "apps/dashboard/list.html") def test_renders_input_form(self): mylist = List.objects.create() - url = f"/dashboard/{mylist.id}/" + url = f"/dashboard/list/{mylist.id}/" response = self.client.get(url) parsed = lxml.html.fromstring(response.content) forms = parsed.cssselect("form[method=POST]") @@ -80,7 +80,7 @@ class ListViewTest(TestCase): other_list = List.objects.create() Item.objects.create(text="other list item", list=other_list) # When/Act - response = self.client.get(f"/dashboard/{correct_list.id}/") + response = self.client.get(f"/dashboard/list/{correct_list.id}/") # Then/Assert self.assertContains(response, "itemey 1") self.assertContains(response, "itemey 2") @@ -91,7 +91,7 @@ class ListViewTest(TestCase): correct_list = List.objects.create() self.client.post( - f"/dashboard/{correct_list.id}/", + f"/dashboard/list/{correct_list.id}/", data={"text": "A new item for an existing list"}, ) @@ -105,16 +105,16 @@ class ListViewTest(TestCase): correct_list = List.objects.create() response = self.client.post( - f"/dashboard/{correct_list.id}/", + f"/dashboard/list/{correct_list.id}/", data={"text": "A new item for an existing list"}, ) - self.assertRedirects(response, f"/dashboard/{correct_list.id}/") + self.assertRedirects(response, f"/dashboard/list/{correct_list.id}/") # Post invalid input helper def post_invalid_input(self): mylist = List.objects.create() - return self.client.post(f"/dashboard/{mylist.id}/", data={"text": ""}) + return self.client.post(f"/dashboard/list/{mylist.id}/", data={"text": ""}) def test_for_invalid_input_nothing_saved_to_db(self): self.post_invalid_input() @@ -140,7 +140,7 @@ class ListViewTest(TestCase): Item.objects.create(list=list1, text="lorem ipsum") response = self.client.post( - f"/dashboard/{list1.id}/", + f"/dashboard/list/{list1.id}/", data={"text": "lorem ipsum"}, ) @@ -189,16 +189,16 @@ class ShareListTest(TestCase): our_list = List.objects.create() alice = User.objects.create(email="alice@example.com") response = self.client.post( - f"/dashboard/{our_list.id}/share_list", + f"/dashboard/list/{our_list.id}/share_list", data={"recipient": "alice@example.com"}, ) - self.assertRedirects(response, f"/dashboard/{our_list.id}/") + self.assertRedirects(response, f"/dashboard/list/{our_list.id}/") def test_post_with_email_adds_user_to_shared_with(self): our_list = List.objects.create() alice = User.objects.create(email="alice@example.com") self.client.post( - f"/dashboard/{our_list.id}/share_list", + f"/dashboard/list/{our_list.id}/share_list", data={"recipient": "alice@example.com"}, ) self.assertIn(alice, our_list.shared_with.all()) @@ -206,10 +206,10 @@ class ShareListTest(TestCase): def test_post_with_nonexistent_email_redirects_to_list(self): our_list = List.objects.create() response = self.client.post( - f"/dashboard/{our_list.id}/share_list", + f"/dashboard/list/{our_list.id}/share_list", data={"recipient": "nobody@example.com"}, ) - self.assertRedirects(response, f"/dashboard/{our_list.id}/") + self.assertRedirects(response, f"/dashboard/list/{our_list.id}/") def test_share_list_does_not_add_owner_as_recipient(self): owner = User.objects.create(email="owner@example.com") diff --git a/src/apps/dashboard/urls.py b/src/apps/dashboard/urls.py index 8520160..cefd498 100644 --- a/src/apps/dashboard/urls.py +++ b/src/apps/dashboard/urls.py @@ -3,7 +3,7 @@ from . import views urlpatterns = [ path('new_list', views.new_list, name='new_list'), - path('/', views.view_list, name='view_list'), + path('list//', views.view_list, name='view_list'), path('users//', views.my_lists, name='my_lists'), - path('/share_list', views.share_list, name="share_list"), + path('list//share_list', views.share_list, name="share_list"), ] diff --git a/src/functional_tests/test_simple_list_creation.py b/src/functional_tests/test_simple_list_creation.py index 2f096bc..5a0b66a 100644 --- a/src/functional_tests/test_simple_list_creation.py +++ b/src/functional_tests/test_simple_list_creation.py @@ -34,7 +34,10 @@ class NewVisitorTest(FunctionalTest): list_page.add_list_item("Buy peacock feathers") edith_dash_url = self.browser.current_url - self.assertRegex(edith_dash_url, '/dashboard/.+') + self.assertRegex( + edith_dash_url, + r'/dashboard/list/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/$', + ) self.browser.delete_all_cookies() @@ -46,7 +49,10 @@ class NewVisitorTest(FunctionalTest): list_page.add_list_item("Buy milk") francis_dash_url = self.browser.current_url - self.assertRegex(francis_dash_url, '/dashboard/.+') + self.assertRegex( + francis_dash_url, + r'/dashboard/list/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/$', + ) self.assertNotEqual(francis_dash_url, edith_dash_url) page_text = self.browser.find_element(By.TAG_NAME, 'body').text