This commit is contained in:
Alvaro Mariano 2024-05-16 20:45:54 -03:00
parent 14f7651c35
commit 6eb74f47c5
9 changed files with 79 additions and 22 deletions

View file

@ -0,0 +1,19 @@
# Generated by Django 4.2.11 on 2024-05-16 22:42
import uuid
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("notifications", "0016_rename_notification_recipient_unread_notificatio_recipie_8bedf2_idx"),
]
operations = [
migrations.AddField(
model_name="notification",
name="uuid",
field=models.UUIDField(default=uuid.uuid4, serialize=False, null=True),
),
]

View file

@ -0,0 +1,22 @@
# Generated by Django 4.2.11 on 2024-05-16 22:50
import uuid
from django.db import migrations
def gen_uuid(apps, schema_editor):
Notification = apps.get_model("notifications", "Notification")
for row in Notification.objects.all():
row.uuid = uuid.uuid4()
row.save(update_fields=["uuid"])
class Migration(migrations.Migration):
dependencies = [
("notifications", "0017_remove_notification_recipient_unread_idx_and_more"),
]
operations = [
migrations.RunPython(gen_uuid, reverse_code=migrations.RunPython.noop),
]

View file

@ -0,0 +1,19 @@
# Generated by Django 4.2.11 on 2024-05-16 22:42
import uuid
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("notifications", "0018_auto_20240516_1750"),
]
operations = [
migrations.AlterField(
model_name="notification",
name="uuid",
field=models.UUIDField(default=uuid.uuid4, editable=False, serialize=False, unique=True),
),
]

View file

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import datetime
from typing import Union
from uuid import uuid4
from django.conf import settings
from django.contrib.contenttypes.fields import GenericForeignKey
@ -54,6 +55,7 @@ class AbstractNotification(models.Model):
"""
uuid = models.UUIDField(unique=True, default=uuid4, editable=False)
recipient = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
@ -136,10 +138,6 @@ class AbstractNotification(models.Model):
return _("%(actor)s %(verb)s %(action_object)s %(timesince)s ago") % ctx
return _("%(actor)s %(verb)s %(timesince)s ago") % ctx
@property
def slug(self):
return self.id
def _mark_as(self, field: str, status: bool) -> None:
if getattr(self, field, None) != status:
setattr(self, field, status)

View file

@ -1,5 +1,5 @@
<div class="alert alert-block alert-{{ notice.level }}">
<a class="close pull-right" href="{% url 'notifications:mark_as' notice.slug 'read' %}">
<a class="close pull-right" href="{% url 'notifications:mark_as' notice.uuid 'read' %}">
<i class="icon-close"></i>
</a>

View file

@ -51,12 +51,6 @@ def test__str__():
assert str(notification.action_object) in notification_str
@pytest.mark.django_db
def test_slug():
notification = NotificationShortFactory()
assert notification.id == notification.slug
@pytest.mark.parametrize(
"field,initial_status,method_name,expected",
(

View file

@ -1,3 +1,5 @@
from uuid import uuid4
import pytest
from django.core.exceptions import ImproperlyConfigured
from django.test import override_settings
@ -20,7 +22,8 @@ VIEW_NAME = "notifications:mark_as"
@pytest.mark.parametrize("status", status_list + wrong_status_list + soft_delete_status_list)
@pytest.mark.django_db
def test_login_required(status, client):
view_url = reverse(VIEW_NAME, args=(1, status))
_id = uuid4()
view_url = reverse(VIEW_NAME, args=(_id, status))
response = client.get(view_url, follow=True)
assert response.status_code == 200
assert len(response.redirect_chain) == 1
@ -31,8 +34,9 @@ def test_login_required(status, client):
@pytest.mark.parametrize("status", status_list + soft_delete_status_list)
@pytest.mark.django_db
def test_notification_not_found(status, client, staff_user):
_id = uuid4()
client.force_login(staff_user)
response = client.get(reverse(VIEW_NAME, args=(9999, status)))
response = client.get(reverse(VIEW_NAME, args=(_id, status)))
assert response.status_code == 404
@ -40,7 +44,8 @@ def test_notification_not_found(status, client, staff_user):
@pytest.mark.django_db
def test_wrong_methods(status, client, staff_user, notifications): # pylint: disable=unused-argument
client.force_login(staff_user)
response = client.get(reverse(VIEW_NAME, args=(1, status)))
notification = staff_user.notifications_notification_related.first()
response = client.get(reverse(VIEW_NAME, args=(notification.uuid, status)))
assert response.status_code == 404
assert response.content.decode() == f'Status "{status}" not exists.'
@ -69,7 +74,7 @@ def test_mark_as(
client.force_login(staff_user)
notification = NotificationFullFactory(recipient=staff_user, **{field: initial_status})
response = client.get(reverse(VIEW_NAME, args=(notification.slug, status)))
response = client.get(reverse(VIEW_NAME, args=(notification.uuid, status)))
assert response.status_code == 302
notification.refresh_from_db()
assert getattr(notification, field) == expected
@ -81,10 +86,10 @@ def test_mark_as_active(client, staff_user, notifications): # pylint: disable=u
notification = NotificationFullFactory(recipient=staff_user, deleted=True)
with pytest.raises(ImproperlyConfigured):
response = client.get(reverse(VIEW_NAME, args=(notification.slug, "active")))
response = client.get(reverse(VIEW_NAME, args=(notification.uuid, "active")))
with override_settings(DJANGO_NOTIFICATIONS_CONFIG={"SOFT_DELETE": True}):
response = client.get(reverse(VIEW_NAME, args=(notification.slug, "active")))
response = client.get(reverse(VIEW_NAME, args=(notification.uuid, "active")))
assert response.status_code == 302
notification.refresh_from_db()
assert notification.deleted is False
@ -96,11 +101,11 @@ def test_mark_as_deleted(client, staff_user, notifications): # pylint: disable=
notification = NotificationFullFactory(recipient=staff_user, deleted=False)
with override_settings(DJANGO_NOTIFICATIONS_CONFIG={"SOFT_DELETE": True}):
response = client.get(reverse(VIEW_NAME, args=(notification.slug, "deleted")))
response = client.get(reverse(VIEW_NAME, args=(notification.uuid, "deleted")))
assert response.status_code == 302
notification.refresh_from_db()
assert notification.deleted is True
response = client.get(reverse(VIEW_NAME, args=(notification.slug, "deleted")))
response = client.get(reverse(VIEW_NAME, args=(notification.uuid, "deleted")))
with pytest.raises(Notification.DoesNotExist):
notification.refresh_from_db()

View file

@ -8,6 +8,6 @@ app_name = "notifications"
urlpatterns = [
path("list/<str:filter_by>/", views.NotificationsList.as_view(), name="list"),
path("mark-all-as/<str:status>/", views.NotificationsMarkAll.as_view(), name="mark_all_as"),
path("mark/<str:slug>/as/<str:status>/", views.NotificationsMarkAs.as_view(), name="mark_as"),
path("mark/<uuid:uuid>/as/<str:status>/", views.NotificationsMarkAs.as_view(), name="mark_as"),
path("api/<str:filter_by>/", views.NotificationsAPI.as_view(), name="api"),
]

View file

@ -68,9 +68,9 @@ class NotificationsMarkAs(NotificationRedirectMixin, RedirectView):
def get(self, request, *args, **kwargs):
status = self.kwargs["status"]
notification_id = self.kwargs["slug"]
notification_uuid = self.kwargs["uuid"]
notification = get_object_or_404(Notification, recipient=request.user, id=notification_id)
notification = get_object_or_404(Notification, recipient=request.user, uuid=notification_uuid)
method = getattr(notification, f"mark_as_{status}", None)
if not method: