prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Django: adapt admin code for 3.x
  It seems that we missed an upgrade to make it fully compatible with Django 3.x
  
  We are using `admin.ACTION_CHECKBOX_NAME` when it was deprecated and it was removed already:
  
  > The compatibility import of django.contrib.admin.helpers.ACTION_CHECKBOX_NAME in django.contrib.admin is removed.
  
  (from https://docs.djangoproject.com/en/4.0/releases/3.1/#id1)
  
  The code lives at https://github.com/readthedocs/readthedocs.org/blob/e94c26074e9abdf7056b4e6502c52f8a6b128055/readthedocs/notifications/views.py#L48
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `readthedocs/notifications/views.py`
  Content:
  ```
  1 """Django views for the notifications app."""
  2 from django.contrib import admin, messages
  3 from django.http import HttpResponseRedirect
  4 from django.views.generic import FormView
  5 
  6 from .forms import SendNotificationForm
  7 
  8 
  9 class SendNotificationView(FormView):
  10 
  11     """
  12     Form view for sending notifications to users from admin pages.
  13 
  14     Accepts the following additional parameters:
  15 
  16     :param queryset: Queryset to use to determine the users to send emails to
  17     :param action_name: Name of the action to pass to the form template,
  18         determines the action to pass back to the admin view
  19     :param notification_classes: List of :py:class:`Notification` classes to
  20         display in the form
  21     """
  22 
  23     form_class = SendNotificationForm
  24     template_name = "notifications/send_notification_form.html"
  25     action_name = "send_email"
  26     notification_classes = []
  27 
  28     def get_form_kwargs(self):
  29         """
  30         Override form kwargs based on input fields.
  31 
  32         The admin posts to this view initially, so detect the send button on
  33         form post variables. Drop additional fields if we see the send button.
  34         """
  35         kwargs = super().get_form_kwargs()
  36         kwargs["notification_classes"] = self.notification_classes
  37         if "send" not in self.request.POST:
  38             kwargs.pop("data", None)
  39             kwargs.pop("files", None)
  40         return kwargs
  41 
  42     def get_initial(self):
  43         """Add selected ids to initial form data."""
  44         initial = super().get_initial()
  45         initial["_selected_action"] = self.request.POST.getlist(
  46             admin.ACTION_CHECKBOX_NAME,
  47         )
  48         return initial
  49 
  50     def form_valid(self, form):
  51         """If form is valid, send notification to recipients."""
  52         count = 0
  53         notification_cls = form.cleaned_data["source"]
  54         for obj in self.get_queryset().all():
  55             for recipient in self.get_object_recipients(obj):
  56                 notification = notification_cls(
  57                     context_object=obj,
  58                     request=self.request,
  59                     user=recipient,
  60                 )
  61                 notification.send()
  62                 count += 1
  63         if count == 0:
  64             self.message_user("No recipients to send to", level=messages.ERROR)
  65         else:
  66             self.message_user("Queued {} messages".format(count))
  67         return HttpResponseRedirect(self.request.get_full_path())
  68 
  69     def get_object_recipients(self, obj):
  70         """
  71         Iterate over queryset objects and return User objects.
  72 
  73         This allows for non-User querysets to pass back a list of Users to send
  74         to. By default, assume we're working with :py:class:`User` objects and
  75         just yield the single object.
  76 
  77         For example, this could be made to return project owners with::
  78 
  79             for owner in AdminPermission.members(project):
  80                 yield owner
  81 
  82         :param obj: object from queryset, type is dependent on model class
  83         :rtype: django.contrib.auth.models.User
  84         """
  85         yield obj
  86 
  87     def get_queryset(self):
  88         return self.kwargs.get("queryset")
  89 
  90     def get_context_data(self, **kwargs):
  91         """Return queryset in context."""
  92         context = super().get_context_data(**kwargs)
  93         recipients = []
  94         for obj in self.get_queryset().all():
  95             recipients.extend(self.get_object_recipients(obj))
  96         context["recipients"] = recipients
  97         context["action_name"] = self.action_name
  98         return context
  99 
  100     def message_user(
  101         self,
  102         message,
  103         level=messages.INFO,
  104         extra_tags="",
  105         fail_silently=False,
  106     ):
  107         """
  108         Implementation of.
  109 
  110         :py:meth:`django.contrib.admin.options.ModelAdmin.message_user`
  111 
  112         Send message through messages framework
  113         """
  114         # TODO generalize this or check if implementation in ModelAdmin is
  115         # usable here
  116         messages.add_message(
  117             self.request,
  118             level,
  119             message,
  120             extra_tags=extra_tags,
  121             fail_silently=fail_silently,
  122         )
  123
  ```
  --- 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/readthedocs/notifications/views.py b/readthedocs/notifications/views.py
  --- a/readthedocs/notifications/views.py
  +++ b/readthedocs/notifications/views.py
  @@ -1,5 +1,5 @@
   """Django views for the notifications app."""
  -from django.contrib import admin, messages
  +from django.contrib import messages
   from django.http import HttpResponseRedirect
   from django.views.generic import FormView
   
  @@ -42,9 +42,7 @@
       def get_initial(self):
           """Add selected ids to initial form data."""
           initial = super().get_initial()
  -        initial["_selected_action"] = self.request.POST.getlist(
  -            admin.ACTION_CHECKBOX_NAME,
  -        )
  +        initial["_selected_action"] = self.request.POST.getlist("_selected_action")
           return initial
   
       def form_valid(self, form):
  
verification_info: {"golden_diff": "diff --git a/readthedocs/notifications/views.py b/readthedocs/notifications/views.py\n--- a/readthedocs/notifications/views.py\n+++ b/readthedocs/notifications/views.py\n@@ -1,5 +1,5 @@\n \"\"\"Django views for the notifications app.\"\"\"\n-from django.contrib import admin, messages\n+from django.contrib import messages\n from django.http import HttpResponseRedirect\n from django.views.generic import FormView\n \n@@ -42,9 +42,7 @@\n     def get_initial(self):\n         \"\"\"Add selected ids to initial form data.\"\"\"\n         initial = super().get_initial()\n-        initial[\"_selected_action\"] = self.request.POST.getlist(\n-            admin.ACTION_CHECKBOX_NAME,\n-        )\n+        initial[\"_selected_action\"] = self.request.POST.getlist(\"_selected_action\")\n         return initial\n \n     def form_valid(self, form):\n", "issue": "Django: adapt admin code for 3.x\nIt seems that we missed an upgrade to make it fully compatible with Django 3.x\r\n\r\nWe are using `admin.ACTION_CHECKBOX_NAME` when it was deprecated and it was removed already:\r\n\r\n> The compatibility import of django.contrib.admin.helpers.ACTION_CHECKBOX_NAME in django.contrib.admin is removed.\r\n\r\n(from https://docs.djangoproject.com/en/4.0/releases/3.1/#id1)\r\n\r\nThe code lives at https://github.com/readthedocs/readthedocs.org/blob/e94c26074e9abdf7056b4e6502c52f8a6b128055/readthedocs/notifications/views.py#L48\n", "before_files": [{"content": "\"\"\"Django views for the notifications app.\"\"\"\nfrom django.contrib import admin, messages\nfrom django.http import HttpResponseRedirect\nfrom django.views.generic import FormView\n\nfrom .forms import SendNotificationForm\n\n\nclass SendNotificationView(FormView):\n\n    \"\"\"\n    Form view for sending notifications to users from admin pages.\n\n    Accepts the following additional parameters:\n\n    :param queryset: Queryset to use to determine the users to send emails to\n    :param action_name: Name of the action to pass to the form template,\n        determines the action to pass back to the admin view\n    :param notification_classes: List of :py:class:`Notification` classes to\n        display in the form\n    \"\"\"\n\n    form_class = SendNotificationForm\n    template_name = \"notifications/send_notification_form.html\"\n    action_name = \"send_email\"\n    notification_classes = []\n\n    def get_form_kwargs(self):\n        \"\"\"\n        Override form kwargs based on input fields.\n\n        The admin posts to this view initially, so detect the send button on\n        form post variables. Drop additional fields if we see the send button.\n        \"\"\"\n        kwargs = super().get_form_kwargs()\n        kwargs[\"notification_classes\"] = self.notification_classes\n        if \"send\" not in self.request.POST:\n            kwargs.pop(\"data\", None)\n            kwargs.pop(\"files\", None)\n        return kwargs\n\n    def get_initial(self):\n        \"\"\"Add selected ids to initial form data.\"\"\"\n        initial = super().get_initial()\n        initial[\"_selected_action\"] = self.request.POST.getlist(\n            admin.ACTION_CHECKBOX_NAME,\n        )\n        return initial\n\n    def form_valid(self, form):\n        \"\"\"If form is valid, send notification to recipients.\"\"\"\n        count = 0\n        notification_cls = form.cleaned_data[\"source\"]\n        for obj in self.get_queryset().all():\n            for recipient in self.get_object_recipients(obj):\n                notification = notification_cls(\n                    context_object=obj,\n                    request=self.request,\n                    user=recipient,\n                )\n                notification.send()\n                count += 1\n        if count == 0:\n            self.message_user(\"No recipients to send to\", level=messages.ERROR)\n        else:\n            self.message_user(\"Queued {} messages\".format(count))\n        return HttpResponseRedirect(self.request.get_full_path())\n\n    def get_object_recipients(self, obj):\n        \"\"\"\n        Iterate over queryset objects and return User objects.\n\n        This allows for non-User querysets to pass back a list of Users to send\n        to. By default, assume we're working with :py:class:`User` objects and\n        just yield the single object.\n\n        For example, this could be made to return project owners with::\n\n            for owner in AdminPermission.members(project):\n                yield owner\n\n        :param obj: object from queryset, type is dependent on model class\n        :rtype: django.contrib.auth.models.User\n        \"\"\"\n        yield obj\n\n    def get_queryset(self):\n        return self.kwargs.get(\"queryset\")\n\n    def get_context_data(self, **kwargs):\n        \"\"\"Return queryset in context.\"\"\"\n        context = super().get_context_data(**kwargs)\n        recipients = []\n        for obj in self.get_queryset().all():\n            recipients.extend(self.get_object_recipients(obj))\n        context[\"recipients\"] = recipients\n        context[\"action_name\"] = self.action_name\n        return context\n\n    def message_user(\n        self,\n        message,\n        level=messages.INFO,\n        extra_tags=\"\",\n        fail_silently=False,\n    ):\n        \"\"\"\n        Implementation of.\n\n        :py:meth:`django.contrib.admin.options.ModelAdmin.message_user`\n\n        Send message through messages framework\n        \"\"\"\n        # TODO generalize this or check if implementation in ModelAdmin is\n        # usable here\n        messages.add_message(\n            self.request,\n            level,\n            message,\n            extra_tags=extra_tags,\n            fail_silently=fail_silently,\n        )\n", "path": "readthedocs/notifications/views.py"}], "after_files": [{"content": "\"\"\"Django views for the notifications app.\"\"\"\nfrom django.contrib import messages\nfrom django.http import HttpResponseRedirect\nfrom django.views.generic import FormView\n\nfrom .forms import SendNotificationForm\n\n\nclass SendNotificationView(FormView):\n\n    \"\"\"\n    Form view for sending notifications to users from admin pages.\n\n    Accepts the following additional parameters:\n\n    :param queryset: Queryset to use to determine the users to send emails to\n    :param action_name: Name of the action to pass to the form template,\n        determines the action to pass back to the admin view\n    :param notification_classes: List of :py:class:`Notification` classes to\n        display in the form\n    \"\"\"\n\n    form_class = SendNotificationForm\n    template_name = \"notifications/send_notification_form.html\"\n    action_name = \"send_email\"\n    notification_classes = []\n\n    def get_form_kwargs(self):\n        \"\"\"\n        Override form kwargs based on input fields.\n\n        The admin posts to this view initially, so detect the send button on\n        form post variables. Drop additional fields if we see the send button.\n        \"\"\"\n        kwargs = super().get_form_kwargs()\n        kwargs[\"notification_classes\"] = self.notification_classes\n        if \"send\" not in self.request.POST:\n            kwargs.pop(\"data\", None)\n            kwargs.pop(\"files\", None)\n        return kwargs\n\n    def get_initial(self):\n        \"\"\"Add selected ids to initial form data.\"\"\"\n        initial = super().get_initial()\n        initial[\"_selected_action\"] = self.request.POST.getlist(\"_selected_action\")\n        return initial\n\n    def form_valid(self, form):\n        \"\"\"If form is valid, send notification to recipients.\"\"\"\n        count = 0\n        notification_cls = form.cleaned_data[\"source\"]\n        for obj in self.get_queryset().all():\n            for recipient in self.get_object_recipients(obj):\n                notification = notification_cls(\n                    context_object=obj,\n                    request=self.request,\n                    user=recipient,\n                )\n                notification.send()\n                count += 1\n        if count == 0:\n            self.message_user(\"No recipients to send to\", level=messages.ERROR)\n        else:\n            self.message_user(\"Queued {} messages\".format(count))\n        return HttpResponseRedirect(self.request.get_full_path())\n\n    def get_object_recipients(self, obj):\n        \"\"\"\n        Iterate over queryset objects and return User objects.\n\n        This allows for non-User querysets to pass back a list of Users to send\n        to. By default, assume we're working with :py:class:`User` objects and\n        just yield the single object.\n\n        For example, this could be made to return project owners with::\n\n            for owner in AdminPermission.members(project):\n                yield owner\n\n        :param obj: object from queryset, type is dependent on model class\n        :rtype: django.contrib.auth.models.User\n        \"\"\"\n        yield obj\n\n    def get_queryset(self):\n        return self.kwargs.get(\"queryset\")\n\n    def get_context_data(self, **kwargs):\n        \"\"\"Return queryset in context.\"\"\"\n        context = super().get_context_data(**kwargs)\n        recipients = []\n        for obj in self.get_queryset().all():\n            recipients.extend(self.get_object_recipients(obj))\n        context[\"recipients\"] = recipients\n        context[\"action_name\"] = self.action_name\n        return context\n\n    def message_user(\n        self,\n        message,\n        level=messages.INFO,\n        extra_tags=\"\",\n        fail_silently=False,\n    ):\n        \"\"\"\n        Implementation of.\n\n        :py:meth:`django.contrib.admin.options.ModelAdmin.message_user`\n\n        Send message through messages framework\n        \"\"\"\n        # TODO generalize this or check if implementation in ModelAdmin is\n        # usable here\n        messages.add_message(\n            self.request,\n            level,\n            message,\n            extra_tags=extra_tags,\n            fail_silently=fail_silently,\n        )\n", "path": "readthedocs/notifications/views.py"}]}