Repository: bookwyrm-social/bookwyrm

Issue:
Following someone does not always work
**Describe the bug**
I am trying to follow some people, but the "following" status does not prevail

**To Reproduce**
Steps to reproduce the behavior:
1. Follow somebody
2. Refresh page
3. The option to follow still shows up
4. See error

**Expected behavior**
If a follow was succesful, it should stay that way

**Screenshots**
For some reason peek decided to take over my mouse so I did a bunch of tabs to get where I needed at the beginning
![Peek 2022-07-08 16-12](https://user-images.githubusercontent.com/18448587/178071100-c27d0475-7c94-42bb-915c-15326c021759.gif)

**Instance**
Bookrastinating.com running 0.4.0

**Additional context**
It may be a problem when following newer instances maybe?

---

**Desktop (please complete the following information):**
    - OS: Fedora
    - Browser: Firefox
    - Version: 102



Discussion:

Resolving patch:
diff --git a/bookwyrm/models/notification.py b/bookwyrm/models/notification.py
--- a/bookwyrm/models/notification.py
+++ b/bookwyrm/models/notification.py
@@ -71,7 +71,9 @@ def notify(cls, user, related_user, **kwargs):
         """Create a notification"""
         if related_user and (not user.local or user == related_user):
             return
-        notification, _ = cls.objects.get_or_create(user=user, **kwargs)
+        notification = cls.objects.filter(user=user, **kwargs).first()
+        if not notification:
+            notification = cls.objects.create(user=user, **kwargs)
         if related_user:
             notification.related_users.add(related_user)
         notification.read = False
diff --git a/bookwyrm/templatetags/interaction.py b/bookwyrm/templatetags/interaction.py
--- a/bookwyrm/templatetags/interaction.py
+++ b/bookwyrm/templatetags/interaction.py
@@ -42,11 +42,11 @@ def get_relationship(context, user_object):
     """caches the relationship between the logged in user and another user"""
     user = context["request"].user
     return get_or_set(
-        f"cached-relationship-{user.id}-{user_object.id}",
+        f"relationship-{user.id}-{user_object.id}",
         get_relationship_name,
         user,
         user_object,
-        timeout=259200,
+        timeout=60 * 60,
     )
 
 
diff --git a/bookwyrm/views/follow.py b/bookwyrm/views/follow.py
--- a/bookwyrm/views/follow.py
+++ b/bookwyrm/views/follow.py
@@ -1,7 +1,9 @@
 """ views for actions you can take in the application """
 import urllib.parse
 import re
+
 from django.contrib.auth.decorators import login_required
+from django.http import HttpResponse
 from django.shortcuts import get_object_or_404, redirect
 from django.template.response import TemplateResponse
 from django.views.decorators.http import require_POST
@@ -13,6 +15,7 @@
     handle_remote_webfinger,
     subscribe_remote_webfinger,
     WebFingerError,
+    is_api_request,
 )
 
 
@@ -34,6 +37,8 @@ def follow(request):
         # that means we should save to trigger a re-broadcast
         follow_request.save()
 
+    if is_api_request(request):
+        return HttpResponse()
     return redirect(to_follow.local_path)
 
 
@@ -58,8 +63,10 @@ def unfollow(request):
     except models.UserFollowRequest.DoesNotExist:
         clear_cache(request.user, to_unfollow)
 
+    if is_api_request(request):
+        return HttpResponse()
     # this is handled with ajax so it shouldn't really matter
-    return redirect(request.headers.get("Referer", "/"))
+    return redirect("/")
 
 
 @login_required


Test patch:
diff --git a/bookwyrm/tests/models/test_notification.py b/bookwyrm/tests/models/test_notification.py
--- a/bookwyrm/tests/models/test_notification.py
+++ b/bookwyrm/tests/models/test_notification.py
@@ -76,6 +76,17 @@ def test_notify_grouping(self):
         notification.refresh_from_db()
         self.assertEqual(notification.related_users.count(), 2)
 
+    def test_notify_grouping_with_dupes(self):
+        """If there are multiple options to group with, don't cause an error"""
+        models.Notification.objects.create(
+            user=self.local_user, notification_type="FAVORITE"
+        )
+        models.Notification.objects.create(
+            user=self.local_user, notification_type="FAVORITE"
+        )
+        models.Notification.notify(self.local_user, None, notification_type="FAVORITE")
+        self.assertEqual(models.Notification.objects.count(), 2)
+
     def test_notify_remote(self):
         """Don't create notifications for remote users"""
         models.Notification.notify(