diff --git a/bookwyrm/models/relationship.py b/bookwyrm/models/relationship.py index e95c38fa..171f4584 100644 --- a/bookwyrm/models/relationship.py +++ b/bookwyrm/models/relationship.py @@ -39,15 +39,14 @@ class UserRelationship(BookWyrmModel): def save(self, *args, **kwargs): """clear the template cache""" - # invalidate the template cache - cache.delete_many( - [ - f"relationship-{self.user_subject.id}-{self.user_object.id}", - f"relationship-{self.user_object.id}-{self.user_subject.id}", - ] - ) + clear_cache(self.user_subject, self.user_object) super().save(*args, **kwargs) + def delete(self, *args, **kwargs): + """clear the template cache""" + clear_cache(self.user_subject, self.user_object) + super().delete(*args, **kwargs) + class Meta: """relationships should be unique""" @@ -90,7 +89,9 @@ class UserFollows(ActivityMixin, UserRelationship): user_object=self.user_subject, ) ).exists(): - raise IntegrityError() + raise IntegrityError( + "Attempting to follow blocked user", self.user_subject, self.user_object + ) # don't broadcast this type of relationship -- accepts and requests # are handled by the UserFollowRequest model super().save(*args, broadcast=False, **kwargs) @@ -98,11 +99,12 @@ class UserFollows(ActivityMixin, UserRelationship): @classmethod def from_request(cls, follow_request): """converts a follow request into a follow relationship""" - return cls.objects.create( + obj, _ = cls.objects.get_or_create( user_subject=follow_request.user_subject, user_object=follow_request.user_object, remote_id=follow_request.remote_id, ) + return obj class UserFollowRequest(ActivitypubMixin, UserRelationship): @@ -133,7 +135,9 @@ class UserFollowRequest(ActivitypubMixin, UserRelationship): user_object=self.user_subject, ) ).exists(): - raise IntegrityError() + raise IntegrityError( + "Attempting to follow blocked user", self.user_subject, self.user_object + ) super().save(*args, **kwargs) if broadcast and self.user_subject.local and not self.user_object.local: @@ -174,7 +178,8 @@ class UserFollowRequest(ActivitypubMixin, UserRelationship): with transaction.atomic(): UserFollows.from_request(self) - self.delete() + if self.id: + self.delete() def reject(self): """generate a Reject for this follow request""" @@ -207,3 +212,13 @@ class UserBlocks(ActivityMixin, UserRelationship): Q(user_subject=self.user_subject, user_object=self.user_object) | Q(user_subject=self.user_object, user_object=self.user_subject) ).delete() + + +def clear_cache(user_subject, user_object): + """clear relationship cache""" + cache.delete_many( + [ + f"relationship-{user_subject.id}-{user_object.id}", + f"relationship-{user_object.id}-{user_subject.id}", + ] + ) diff --git a/bookwyrm/views/follow.py b/bookwyrm/views/follow.py index 6e58a2b2..8bbcfca8 100644 --- a/bookwyrm/views/follow.py +++ b/bookwyrm/views/follow.py @@ -2,12 +2,12 @@ import urllib.parse import re from django.contrib.auth.decorators import login_required -from django.db import IntegrityError from django.shortcuts import get_object_or_404, redirect from django.template.response import TemplateResponse from django.views.decorators.http import require_POST from bookwyrm import models +from bookwyrm.models.relationship import clear_cache from .helpers import ( get_user_from_username, handle_remote_webfinger, @@ -22,17 +22,17 @@ def follow(request): """follow another user, here or abroad""" username = request.POST["user"] to_follow = get_user_from_username(request.user, username) + clear_cache(request.user, to_follow) - try: - models.UserFollowRequest.objects.create( - user_subject=request.user, - user_object=to_follow, - ) - except IntegrityError: - pass + follow_request, created = models.UserFollowRequest.objects.get_or_create( + user_subject=request.user, + user_object=to_follow, + ) - if request.GET.get("next"): - return redirect(request.GET.get("next", "/")) + if not created: + # this request probably failed to connect with the remote + # that means we should save to trigger a re-broadcast + follow_request.save() return redirect(to_follow.local_path) @@ -49,14 +49,14 @@ def unfollow(request): user_subject=request.user, user_object=to_unfollow ).delete() except models.UserFollows.DoesNotExist: - pass + clear_cache(request.user, to_unfollow) try: models.UserFollowRequest.objects.get( user_subject=request.user, user_object=to_unfollow ).delete() except models.UserFollowRequest.DoesNotExist: - pass + clear_cache(request.user, to_unfollow) # this is handled with ajax so it shouldn't really matter return redirect(request.headers.get("Referer", "/"))