prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Still get notifications for follows from blocked user
  **Describe the bug**
  I blocked https://bookwyrm.social/user/followbot@search.fedi.app, but I'm still getting follow notifications from them. I also can still see them in my followers list
  
  **To Reproduce**
  1. Block user that currently follows you
  2. View own follow list, see that they still follow you
  3. Have that user (while blocked) refollow you
  4. See notification for that follow
  
  **Expected behavior**
  I expect that a block would force a user to unfollow me. Even if this didn't happen, though, I'd like to no longer receive notifications from activity of users that I've blocked, including follow notifications
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `bookwyrm/models/relationship.py`
  Content:
  ```
  1 ''' defines relationships between users '''
  2 from django.apps import apps
  3 from django.db import models, transaction
  4 from django.db.models import Q
  5 from django.dispatch import receiver
  6 
  7 from bookwyrm import activitypub
  8 from .activitypub_mixin import ActivitypubMixin, ActivityMixin
  9 from .base_model import BookWyrmModel
  10 from . import fields
  11 
  12 
  13 class UserRelationship(BookWyrmModel):
  14     ''' many-to-many through table for followers '''
  15     user_subject = fields.ForeignKey(
  16         'User',
  17         on_delete=models.PROTECT,
  18         related_name='%(class)s_user_subject',
  19         activitypub_field='actor',
  20     )
  21     user_object = fields.ForeignKey(
  22         'User',
  23         on_delete=models.PROTECT,
  24         related_name='%(class)s_user_object',
  25         activitypub_field='object',
  26     )
  27 
  28     @property
  29     def privacy(self):
  30         ''' all relationships are handled directly with the participants '''
  31         return 'direct'
  32 
  33     @property
  34     def recipients(self):
  35         ''' the remote user needs to recieve direct broadcasts '''
  36         return [u for u in [self.user_subject, self.user_object] if not u.local]
  37 
  38     class Meta:
  39         ''' relationships should be unique '''
  40         abstract = True
  41         constraints = [
  42             models.UniqueConstraint(
  43                 fields=['user_subject', 'user_object'],
  44                 name='%(class)s_unique'
  45             ),
  46             models.CheckConstraint(
  47                 check=~models.Q(user_subject=models.F('user_object')),
  48                 name='%(class)s_no_self'
  49             )
  50         ]
  51 
  52     def get_remote_id(self, status=None):# pylint: disable=arguments-differ
  53         ''' use shelf identifier in remote_id '''
  54         status = status or 'follows'
  55         base_path = self.user_subject.remote_id
  56         return '%s#%s/%d' % (base_path, status, self.id)
  57 
  58 
  59 class UserFollows(ActivitypubMixin, UserRelationship):
  60     ''' Following a user '''
  61     status = 'follows'
  62     activity_serializer = activitypub.Follow
  63 
  64 
  65     @classmethod
  66     def from_request(cls, follow_request):
  67         ''' converts a follow request into a follow relationship '''
  68         return cls.objects.create(
  69             user_subject=follow_request.user_subject,
  70             user_object=follow_request.user_object,
  71             remote_id=follow_request.remote_id,
  72         )
  73 
  74 
  75 class UserFollowRequest(ActivitypubMixin, UserRelationship):
  76     ''' following a user requires manual or automatic confirmation '''
  77     status = 'follow_request'
  78     activity_serializer = activitypub.Follow
  79 
  80     def save(self, *args, broadcast=True, **kwargs):
  81         ''' make sure the follow or block relationship doesn't already exist '''
  82         try:
  83             UserFollows.objects.get(
  84                 user_subject=self.user_subject,
  85                 user_object=self.user_object
  86             )
  87             UserBlocks.objects.get(
  88                 user_subject=self.user_subject,
  89                 user_object=self.user_object
  90             )
  91             return None
  92         except (UserFollows.DoesNotExist, UserBlocks.DoesNotExist):
  93             super().save(*args, **kwargs)
  94 
  95         if broadcast and self.user_subject.local and not self.user_object.local:
  96             self.broadcast(self.to_activity(), self.user_subject)
  97 
  98         if self.user_object.local:
  99             model = apps.get_model('bookwyrm.Notification', require_ready=True)
  100             notification_type = 'FOLLOW_REQUEST' \
  101                 if self.user_object.manually_approves_followers else 'FOLLOW'
  102             model.objects.create(
  103                 user=self.user_object,
  104                 related_user=self.user_subject,
  105                 notification_type=notification_type,
  106             )
  107 
  108 
  109     def accept(self):
  110         ''' turn this request into the real deal'''
  111         user = self.user_object
  112         activity = activitypub.Accept(
  113             id=self.get_remote_id(status='accepts'),
  114             actor=self.user_object.remote_id,
  115             object=self.to_activity()
  116         ).serialize()
  117         with transaction.atomic():
  118             UserFollows.from_request(self)
  119             self.delete()
  120 
  121         self.broadcast(activity, user)
  122 
  123 
  124     def reject(self):
  125         ''' generate a Reject for this follow request '''
  126         user = self.user_object
  127         activity = activitypub.Reject(
  128             id=self.get_remote_id(status='rejects'),
  129             actor=self.user_object.remote_id,
  130             object=self.to_activity()
  131         ).serialize()
  132         self.delete()
  133         self.broadcast(activity, user)
  134 
  135 
  136 class UserBlocks(ActivityMixin, UserRelationship):
  137     ''' prevent another user from following you and seeing your posts '''
  138     status = 'blocks'
  139     activity_serializer = activitypub.Block
  140 
  141 
  142 @receiver(models.signals.post_save, sender=UserBlocks)
  143 #pylint: disable=unused-argument
  144 def execute_after_save(sender, instance, created, *args, **kwargs):
  145     ''' remove follow or follow request rels after a block is created '''
  146     UserFollows.objects.filter(
  147         Q(user_subject=instance.user_subject,
  148           user_object=instance.user_object) | \
  149         Q(user_subject=instance.user_object,
  150           user_object=instance.user_subject)
  151     ).delete()
  152     UserFollowRequest.objects.filter(
  153         Q(user_subject=instance.user_subject,
  154           user_object=instance.user_object) | \
  155         Q(user_subject=instance.user_object,
  156           user_object=instance.user_subject)
  157     ).delete()
  158
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/bookwyrm/models/relationship.py b/bookwyrm/models/relationship.py
  --- a/bookwyrm/models/relationship.py
  +++ b/bookwyrm/models/relationship.py
  @@ -82,11 +82,16 @@
           try:
               UserFollows.objects.get(
                   user_subject=self.user_subject,
  -                user_object=self.user_object
  +                user_object=self.user_object,
               )
  +            # blocking in either direction is a no-go
               UserBlocks.objects.get(
                   user_subject=self.user_subject,
  -                user_object=self.user_object
  +                user_object=self.user_object,
  +            )
  +            UserBlocks.objects.get(
  +                user_subject=self.user_object,
  +                user_object=self.user_subject,
               )
               return None
           except (UserFollows.DoesNotExist, UserBlocks.DoesNotExist):
  
verification_info: {"golden_diff": "diff --git a/bookwyrm/models/relationship.py b/bookwyrm/models/relationship.py\n--- a/bookwyrm/models/relationship.py\n+++ b/bookwyrm/models/relationship.py\n@@ -82,11 +82,16 @@\n         try:\n             UserFollows.objects.get(\n                 user_subject=self.user_subject,\n-                user_object=self.user_object\n+                user_object=self.user_object,\n             )\n+            # blocking in either direction is a no-go\n             UserBlocks.objects.get(\n                 user_subject=self.user_subject,\n-                user_object=self.user_object\n+                user_object=self.user_object,\n+            )\n+            UserBlocks.objects.get(\n+                user_subject=self.user_object,\n+                user_object=self.user_subject,\n             )\n             return None\n         except (UserFollows.DoesNotExist, UserBlocks.DoesNotExist):\n", "issue": "Still get notifications for follows from blocked user\n**Describe the bug**\r\nI blocked https://bookwyrm.social/user/followbot@search.fedi.app, but I'm still getting follow notifications from them. I also can still see them in my followers list\r\n\r\n**To Reproduce**\r\n1. Block user that currently follows you\r\n2. View own follow list, see that they still follow you\r\n3. Have that user (while blocked) refollow you\r\n4. See notification for that follow\r\n\r\n**Expected behavior**\r\nI expect that a block would force a user to unfollow me. Even if this didn't happen, though, I'd like to no longer receive notifications from activity of users that I've blocked, including follow notifications\n", "before_files": [{"content": "''' defines relationships between users '''\nfrom django.apps import apps\nfrom django.db import models, transaction\nfrom django.db.models import Q\nfrom django.dispatch import receiver\n\nfrom bookwyrm import activitypub\nfrom .activitypub_mixin import ActivitypubMixin, ActivityMixin\nfrom .base_model import BookWyrmModel\nfrom . import fields\n\n\nclass UserRelationship(BookWyrmModel):\n    ''' many-to-many through table for followers '''\n    user_subject = fields.ForeignKey(\n        'User',\n        on_delete=models.PROTECT,\n        related_name='%(class)s_user_subject',\n        activitypub_field='actor',\n    )\n    user_object = fields.ForeignKey(\n        'User',\n        on_delete=models.PROTECT,\n        related_name='%(class)s_user_object',\n        activitypub_field='object',\n    )\n\n    @property\n    def privacy(self):\n        ''' all relationships are handled directly with the participants '''\n        return 'direct'\n\n    @property\n    def recipients(self):\n        ''' the remote user needs to recieve direct broadcasts '''\n        return [u for u in [self.user_subject, self.user_object] if not u.local]\n\n    class Meta:\n        ''' relationships should be unique '''\n        abstract = True\n        constraints = [\n            models.UniqueConstraint(\n                fields=['user_subject', 'user_object'],\n                name='%(class)s_unique'\n            ),\n            models.CheckConstraint(\n                check=~models.Q(user_subject=models.F('user_object')),\n                name='%(class)s_no_self'\n            )\n        ]\n\n    def get_remote_id(self, status=None):# pylint: disable=arguments-differ\n        ''' use shelf identifier in remote_id '''\n        status = status or 'follows'\n        base_path = self.user_subject.remote_id\n        return '%s#%s/%d' % (base_path, status, self.id)\n\n\nclass UserFollows(ActivitypubMixin, UserRelationship):\n    ''' Following a user '''\n    status = 'follows'\n    activity_serializer = activitypub.Follow\n\n\n    @classmethod\n    def from_request(cls, follow_request):\n        ''' converts a follow request into a follow relationship '''\n        return cls.objects.create(\n            user_subject=follow_request.user_subject,\n            user_object=follow_request.user_object,\n            remote_id=follow_request.remote_id,\n        )\n\n\nclass UserFollowRequest(ActivitypubMixin, UserRelationship):\n    ''' following a user requires manual or automatic confirmation '''\n    status = 'follow_request'\n    activity_serializer = activitypub.Follow\n\n    def save(self, *args, broadcast=True, **kwargs):\n        ''' make sure the follow or block relationship doesn't already exist '''\n        try:\n            UserFollows.objects.get(\n                user_subject=self.user_subject,\n                user_object=self.user_object\n            )\n            UserBlocks.objects.get(\n                user_subject=self.user_subject,\n                user_object=self.user_object\n            )\n            return None\n        except (UserFollows.DoesNotExist, UserBlocks.DoesNotExist):\n            super().save(*args, **kwargs)\n\n        if broadcast and self.user_subject.local and not self.user_object.local:\n            self.broadcast(self.to_activity(), self.user_subject)\n\n        if self.user_object.local:\n            model = apps.get_model('bookwyrm.Notification', require_ready=True)\n            notification_type = 'FOLLOW_REQUEST' \\\n                if self.user_object.manually_approves_followers else 'FOLLOW'\n            model.objects.create(\n                user=self.user_object,\n                related_user=self.user_subject,\n                notification_type=notification_type,\n            )\n\n\n    def accept(self):\n        ''' turn this request into the real deal'''\n        user = self.user_object\n        activity = activitypub.Accept(\n            id=self.get_remote_id(status='accepts'),\n            actor=self.user_object.remote_id,\n            object=self.to_activity()\n        ).serialize()\n        with transaction.atomic():\n            UserFollows.from_request(self)\n            self.delete()\n\n        self.broadcast(activity, user)\n\n\n    def reject(self):\n        ''' generate a Reject for this follow request '''\n        user = self.user_object\n        activity = activitypub.Reject(\n            id=self.get_remote_id(status='rejects'),\n            actor=self.user_object.remote_id,\n            object=self.to_activity()\n        ).serialize()\n        self.delete()\n        self.broadcast(activity, user)\n\n\nclass UserBlocks(ActivityMixin, UserRelationship):\n    ''' prevent another user from following you and seeing your posts '''\n    status = 'blocks'\n    activity_serializer = activitypub.Block\n\n\n@receiver(models.signals.post_save, sender=UserBlocks)\n#pylint: disable=unused-argument\ndef execute_after_save(sender, instance, created, *args, **kwargs):\n    ''' remove follow or follow request rels after a block is created '''\n    UserFollows.objects.filter(\n        Q(user_subject=instance.user_subject,\n          user_object=instance.user_object) | \\\n        Q(user_subject=instance.user_object,\n          user_object=instance.user_subject)\n    ).delete()\n    UserFollowRequest.objects.filter(\n        Q(user_subject=instance.user_subject,\n          user_object=instance.user_object) | \\\n        Q(user_subject=instance.user_object,\n          user_object=instance.user_subject)\n    ).delete()\n", "path": "bookwyrm/models/relationship.py"}], "after_files": [{"content": "''' defines relationships between users '''\nfrom django.apps import apps\nfrom django.db import models, transaction\nfrom django.db.models import Q\nfrom django.dispatch import receiver\n\nfrom bookwyrm import activitypub\nfrom .activitypub_mixin import ActivitypubMixin, ActivityMixin\nfrom .base_model import BookWyrmModel\nfrom . import fields\n\n\nclass UserRelationship(BookWyrmModel):\n    ''' many-to-many through table for followers '''\n    user_subject = fields.ForeignKey(\n        'User',\n        on_delete=models.PROTECT,\n        related_name='%(class)s_user_subject',\n        activitypub_field='actor',\n    )\n    user_object = fields.ForeignKey(\n        'User',\n        on_delete=models.PROTECT,\n        related_name='%(class)s_user_object',\n        activitypub_field='object',\n    )\n\n    @property\n    def privacy(self):\n        ''' all relationships are handled directly with the participants '''\n        return 'direct'\n\n    @property\n    def recipients(self):\n        ''' the remote user needs to recieve direct broadcasts '''\n        return [u for u in [self.user_subject, self.user_object] if not u.local]\n\n    class Meta:\n        ''' relationships should be unique '''\n        abstract = True\n        constraints = [\n            models.UniqueConstraint(\n                fields=['user_subject', 'user_object'],\n                name='%(class)s_unique'\n            ),\n            models.CheckConstraint(\n                check=~models.Q(user_subject=models.F('user_object')),\n                name='%(class)s_no_self'\n            )\n        ]\n\n    def get_remote_id(self, status=None):# pylint: disable=arguments-differ\n        ''' use shelf identifier in remote_id '''\n        status = status or 'follows'\n        base_path = self.user_subject.remote_id\n        return '%s#%s/%d' % (base_path, status, self.id)\n\n\nclass UserFollows(ActivitypubMixin, UserRelationship):\n    ''' Following a user '''\n    status = 'follows'\n    activity_serializer = activitypub.Follow\n\n\n    @classmethod\n    def from_request(cls, follow_request):\n        ''' converts a follow request into a follow relationship '''\n        return cls.objects.create(\n            user_subject=follow_request.user_subject,\n            user_object=follow_request.user_object,\n            remote_id=follow_request.remote_id,\n        )\n\n\nclass UserFollowRequest(ActivitypubMixin, UserRelationship):\n    ''' following a user requires manual or automatic confirmation '''\n    status = 'follow_request'\n    activity_serializer = activitypub.Follow\n\n    def save(self, *args, broadcast=True, **kwargs):\n        ''' make sure the follow or block relationship doesn't already exist '''\n        try:\n            UserFollows.objects.get(\n                user_subject=self.user_subject,\n                user_object=self.user_object,\n            )\n            # blocking in either direction is a no-go\n            UserBlocks.objects.get(\n                user_subject=self.user_subject,\n                user_object=self.user_object,\n            )\n            UserBlocks.objects.get(\n                user_subject=self.user_object,\n                user_object=self.user_subject,\n            )\n            return None\n        except (UserFollows.DoesNotExist, UserBlocks.DoesNotExist):\n            super().save(*args, **kwargs)\n\n        if broadcast and self.user_subject.local and not self.user_object.local:\n            self.broadcast(self.to_activity(), self.user_subject)\n\n        if self.user_object.local:\n            model = apps.get_model('bookwyrm.Notification', require_ready=True)\n            notification_type = 'FOLLOW_REQUEST' \\\n                if self.user_object.manually_approves_followers else 'FOLLOW'\n            model.objects.create(\n                user=self.user_object,\n                related_user=self.user_subject,\n                notification_type=notification_type,\n            )\n\n\n    def accept(self):\n        ''' turn this request into the real deal'''\n        user = self.user_object\n        activity = activitypub.Accept(\n            id=self.get_remote_id(status='accepts'),\n            actor=self.user_object.remote_id,\n            object=self.to_activity()\n        ).serialize()\n        with transaction.atomic():\n            UserFollows.from_request(self)\n            self.delete()\n\n        self.broadcast(activity, user)\n\n\n    def reject(self):\n        ''' generate a Reject for this follow request '''\n        user = self.user_object\n        activity = activitypub.Reject(\n            id=self.get_remote_id(status='rejects'),\n            actor=self.user_object.remote_id,\n            object=self.to_activity()\n        ).serialize()\n        self.delete()\n        self.broadcast(activity, user)\n\n\nclass UserBlocks(ActivityMixin, UserRelationship):\n    ''' prevent another user from following you and seeing your posts '''\n    status = 'blocks'\n    activity_serializer = activitypub.Block\n\n\n@receiver(models.signals.post_save, sender=UserBlocks)\n#pylint: disable=unused-argument\ndef execute_after_save(sender, instance, created, *args, **kwargs):\n    ''' remove follow or follow request rels after a block is created '''\n    UserFollows.objects.filter(\n        Q(user_subject=instance.user_subject,\n          user_object=instance.user_object) | \\\n        Q(user_subject=instance.user_object,\n          user_object=instance.user_subject)\n    ).delete()\n    UserFollowRequest.objects.filter(\n        Q(user_subject=instance.user_subject,\n          user_object=instance.user_object) | \\\n        Q(user_subject=instance.user_object,\n          user_object=instance.user_subject)\n    ).delete()\n", "path": "bookwyrm/models/relationship.py"}]}