prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Use XHR for user select2 widgets
  on a site with a lot of users the admin widgets are very slow and unnecessarily push a full list of users
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `pootle/apps/pootle_app/views/admin/permissions.py`
  Content:
  ```
  1 # -*- coding: utf-8 -*-
  2 #
  3 # Copyright (C) Pootle contributors.
  4 #
  5 # This file is a part of the Pootle project. It is distributed under the GPL3
  6 # or later license. See the LICENSE file for a copy of the license and the
  7 # AUTHORS file for copyright and authorship information.
  8 
  9 from django import forms
  10 from django.contrib.auth import get_user_model
  11 
  12 from pootle.core.decorators import get_object_or_404
  13 from pootle.core.exceptions import Http400
  14 from pootle.core.views.admin import PootleAdminFormView
  15 from pootle.core.views.mixins import PootleJSONMixin
  16 from pootle.i18n.gettext import ugettext as _
  17 from pootle_app.forms import PermissionsUsersSearchForm
  18 from pootle_app.models import Directory
  19 from pootle_app.models.permissions import (PermissionSet,
  20                                            get_permission_contenttype)
  21 from pootle_app.views.admin import util
  22 from pootle_misc.forms import GroupedModelChoiceField
  23 
  24 
  25 User = get_user_model()
  26 
  27 PERMISSIONS = {
  28     'positive': ['view', 'suggest', 'translate', 'review', 'administrate'],
  29     'negative': ['hide'],
  30 }
  31 
  32 
  33 class PermissionFormField(forms.ModelMultipleChoiceField):
  34 
  35     def label_from_instance(self, instance):
  36         return _(instance.name)
  37 
  38 
  39 def admin_permissions(request, current_directory, template, ctx):
  40     language = ctx.get('language', None)
  41 
  42     negative_permissions_excl = list(PERMISSIONS['negative'])
  43     positive_permissions_excl = list(PERMISSIONS['positive'])
  44 
  45     # Don't provide means to alter access permissions under /<lang_code>/*
  46     # In other words: only allow setting access permissions for the root
  47     # and the `/projects/<code>/` directories
  48     if language is not None:
  49         access_permissions = ['view', 'hide']
  50         negative_permissions_excl.extend(access_permissions)
  51         positive_permissions_excl.extend(access_permissions)
  52 
  53     content_type = get_permission_contenttype()
  54 
  55     positive_permissions_qs = content_type.permission_set.exclude(
  56         codename__in=negative_permissions_excl,
  57     )
  58     negative_permissions_qs = content_type.permission_set.exclude(
  59         codename__in=positive_permissions_excl,
  60     )
  61 
  62     base_queryset = User.objects.filter(is_active=1).exclude(
  63         id__in=current_directory.permission_sets.values_list('user_id',
  64                                                              flat=True),)
  65     choice_groups = [(None, base_queryset.filter(
  66         username__in=('nobody', 'default')
  67     ))]
  68 
  69     choice_groups.append((
  70         _('All Users'),
  71         base_queryset.exclude(username__in=('nobody',
  72                                             'default')).order_by('username'),
  73     ))
  74 
  75     class PermissionSetForm(forms.ModelForm):
  76 
  77         class Meta(object):
  78             model = PermissionSet
  79             fields = ('user', 'directory', 'positive_permissions',
  80                       'negative_permissions')
  81 
  82         directory = forms.ModelChoiceField(
  83             queryset=Directory.objects.filter(pk=current_directory.pk),
  84             initial=current_directory.pk,
  85             widget=forms.HiddenInput,
  86         )
  87         user = GroupedModelChoiceField(
  88             label=_('Username'),
  89             choice_groups=choice_groups,
  90             queryset=User.objects.all(),
  91             required=True,
  92             widget=forms.Select(attrs={
  93                 'class': 'js-select2 select2-username',
  94             }),
  95         )
  96         positive_permissions = PermissionFormField(
  97             label=_('Add Permissions'),
  98             queryset=positive_permissions_qs,
  99             required=False,
  100             widget=forms.SelectMultiple(attrs={
  101                 'class': 'js-select2 select2-multiple',
  102                 'data-placeholder': _('Select one or more permissions'),
  103             }),
  104         )
  105         negative_permissions = PermissionFormField(
  106             label=_('Revoke Permissions'),
  107             queryset=negative_permissions_qs,
  108             required=False,
  109             widget=forms.SelectMultiple(attrs={
  110                 'class': 'js-select2 select2-multiple',
  111                 'data-placeholder': _('Select one or more permissions'),
  112             }),
  113         )
  114 
  115         def __init__(self, *args, **kwargs):
  116             super(PermissionSetForm, self).__init__(*args, **kwargs)
  117 
  118             # Don't display extra negative permissions field where they
  119             # are not applicable
  120             if language is not None:
  121                 del self.fields['negative_permissions']
  122 
  123     link = lambda instance: unicode(instance.user)
  124     directory_permissions = current_directory.permission_sets \
  125                                              .order_by('user').all()
  126 
  127     return util.edit(request, template, PermissionSet, ctx, link,
  128                      linkfield='user', queryset=directory_permissions,
  129                      can_delete=True, form=PermissionSetForm)
  130 
  131 
  132 class PermissionsUsersJSON(PootleJSONMixin, PootleAdminFormView):
  133     form_class = PermissionsUsersSearchForm
  134 
  135     @property
  136     def directory(self):
  137         return get_object_or_404(
  138             Directory.objects,
  139             pk=self.kwargs["directory"])
  140 
  141     def get_context_data(self, **kwargs):
  142         context = super(
  143             PermissionsUsersJSON, self).get_context_data(**kwargs)
  144         form = context["form"]
  145         return (
  146             dict(items=form.search())
  147             if form.is_valid()
  148             else dict(items=[]))
  149 
  150     def get_form_kwargs(self):
  151         kwargs = super(PermissionsUsersJSON, self).get_form_kwargs()
  152         kwargs["directory"] = self.directory
  153         return kwargs
  154 
  155     def form_valid(self, form):
  156         return self.render_to_response(
  157             self.get_context_data(form=form))
  158 
  159     def form_invalid(self, form):
  160         raise Http400(form.errors)
  161
  ```
  --- 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/pootle/apps/pootle_app/views/admin/permissions.py b/pootle/apps/pootle_app/views/admin/permissions.py
  --- a/pootle/apps/pootle_app/views/admin/permissions.py
  +++ b/pootle/apps/pootle_app/views/admin/permissions.py
  @@ -8,11 +8,13 @@
   
   from django import forms
   from django.contrib.auth import get_user_model
  +from django.urls import reverse
   
   from pootle.core.decorators import get_object_or_404
   from pootle.core.exceptions import Http400
   from pootle.core.views.admin import PootleAdminFormView
   from pootle.core.views.mixins import PootleJSONMixin
  +from pootle.core.views.widgets import RemoteSelectWidget
   from pootle.i18n.gettext import ugettext as _
   from pootle_app.forms import PermissionsUsersSearchForm
   from pootle_app.models import Directory
  @@ -89,10 +91,11 @@
               choice_groups=choice_groups,
               queryset=User.objects.all(),
               required=True,
  -            widget=forms.Select(attrs={
  -                'class': 'js-select2 select2-username',
  -            }),
  -        )
  +            widget=RemoteSelectWidget(
  +                attrs={
  +                    "data-s2-placeholder": _("Search for users to add"),
  +                    'class': ('js-select2-remote select2-username '
  +                              'js-s2-new-members')}))
           positive_permissions = PermissionFormField(
               label=_('Add Permissions'),
               queryset=positive_permissions_qs,
  @@ -115,6 +118,10 @@
           def __init__(self, *args, **kwargs):
               super(PermissionSetForm, self).__init__(*args, **kwargs)
   
  +            self.fields["user"].widget.attrs["data-select2-url"] = reverse(
  +                "pootle-permissions-users",
  +                kwargs=dict(directory=current_directory.pk))
  +
               # Don't display extra negative permissions field where they
               # are not applicable
               if language is not None:
  
verification_info: {"golden_diff": "diff --git a/pootle/apps/pootle_app/views/admin/permissions.py b/pootle/apps/pootle_app/views/admin/permissions.py\n--- a/pootle/apps/pootle_app/views/admin/permissions.py\n+++ b/pootle/apps/pootle_app/views/admin/permissions.py\n@@ -8,11 +8,13 @@\n \n from django import forms\n from django.contrib.auth import get_user_model\n+from django.urls import reverse\n \n from pootle.core.decorators import get_object_or_404\n from pootle.core.exceptions import Http400\n from pootle.core.views.admin import PootleAdminFormView\n from pootle.core.views.mixins import PootleJSONMixin\n+from pootle.core.views.widgets import RemoteSelectWidget\n from pootle.i18n.gettext import ugettext as _\n from pootle_app.forms import PermissionsUsersSearchForm\n from pootle_app.models import Directory\n@@ -89,10 +91,11 @@\n             choice_groups=choice_groups,\n             queryset=User.objects.all(),\n             required=True,\n-            widget=forms.Select(attrs={\n-                'class': 'js-select2 select2-username',\n-            }),\n-        )\n+            widget=RemoteSelectWidget(\n+                attrs={\n+                    \"data-s2-placeholder\": _(\"Search for users to add\"),\n+                    'class': ('js-select2-remote select2-username '\n+                              'js-s2-new-members')}))\n         positive_permissions = PermissionFormField(\n             label=_('Add Permissions'),\n             queryset=positive_permissions_qs,\n@@ -115,6 +118,10 @@\n         def __init__(self, *args, **kwargs):\n             super(PermissionSetForm, self).__init__(*args, **kwargs)\n \n+            self.fields[\"user\"].widget.attrs[\"data-select2-url\"] = reverse(\n+                \"pootle-permissions-users\",\n+                kwargs=dict(directory=current_directory.pk))\n+\n             # Don't display extra negative permissions field where they\n             # are not applicable\n             if language is not None:\n", "issue": "Use XHR for user select2 widgets\non a site with a lot of users the admin widgets are very slow and unnecessarily push a full list of users\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n#\n# Copyright (C) Pootle contributors.\n#\n# This file is a part of the Pootle project. It is distributed under the GPL3\n# or later license. See the LICENSE file for a copy of the license and the\n# AUTHORS file for copyright and authorship information.\n\nfrom django import forms\nfrom django.contrib.auth import get_user_model\n\nfrom pootle.core.decorators import get_object_or_404\nfrom pootle.core.exceptions import Http400\nfrom pootle.core.views.admin import PootleAdminFormView\nfrom pootle.core.views.mixins import PootleJSONMixin\nfrom pootle.i18n.gettext import ugettext as _\nfrom pootle_app.forms import PermissionsUsersSearchForm\nfrom pootle_app.models import Directory\nfrom pootle_app.models.permissions import (PermissionSet,\n                                           get_permission_contenttype)\nfrom pootle_app.views.admin import util\nfrom pootle_misc.forms import GroupedModelChoiceField\n\n\nUser = get_user_model()\n\nPERMISSIONS = {\n    'positive': ['view', 'suggest', 'translate', 'review', 'administrate'],\n    'negative': ['hide'],\n}\n\n\nclass PermissionFormField(forms.ModelMultipleChoiceField):\n\n    def label_from_instance(self, instance):\n        return _(instance.name)\n\n\ndef admin_permissions(request, current_directory, template, ctx):\n    language = ctx.get('language', None)\n\n    negative_permissions_excl = list(PERMISSIONS['negative'])\n    positive_permissions_excl = list(PERMISSIONS['positive'])\n\n    # Don't provide means to alter access permissions under /<lang_code>/*\n    # In other words: only allow setting access permissions for the root\n    # and the `/projects/<code>/` directories\n    if language is not None:\n        access_permissions = ['view', 'hide']\n        negative_permissions_excl.extend(access_permissions)\n        positive_permissions_excl.extend(access_permissions)\n\n    content_type = get_permission_contenttype()\n\n    positive_permissions_qs = content_type.permission_set.exclude(\n        codename__in=negative_permissions_excl,\n    )\n    negative_permissions_qs = content_type.permission_set.exclude(\n        codename__in=positive_permissions_excl,\n    )\n\n    base_queryset = User.objects.filter(is_active=1).exclude(\n        id__in=current_directory.permission_sets.values_list('user_id',\n                                                             flat=True),)\n    choice_groups = [(None, base_queryset.filter(\n        username__in=('nobody', 'default')\n    ))]\n\n    choice_groups.append((\n        _('All Users'),\n        base_queryset.exclude(username__in=('nobody',\n                                            'default')).order_by('username'),\n    ))\n\n    class PermissionSetForm(forms.ModelForm):\n\n        class Meta(object):\n            model = PermissionSet\n            fields = ('user', 'directory', 'positive_permissions',\n                      'negative_permissions')\n\n        directory = forms.ModelChoiceField(\n            queryset=Directory.objects.filter(pk=current_directory.pk),\n            initial=current_directory.pk,\n            widget=forms.HiddenInput,\n        )\n        user = GroupedModelChoiceField(\n            label=_('Username'),\n            choice_groups=choice_groups,\n            queryset=User.objects.all(),\n            required=True,\n            widget=forms.Select(attrs={\n                'class': 'js-select2 select2-username',\n            }),\n        )\n        positive_permissions = PermissionFormField(\n            label=_('Add Permissions'),\n            queryset=positive_permissions_qs,\n            required=False,\n            widget=forms.SelectMultiple(attrs={\n                'class': 'js-select2 select2-multiple',\n                'data-placeholder': _('Select one or more permissions'),\n            }),\n        )\n        negative_permissions = PermissionFormField(\n            label=_('Revoke Permissions'),\n            queryset=negative_permissions_qs,\n            required=False,\n            widget=forms.SelectMultiple(attrs={\n                'class': 'js-select2 select2-multiple',\n                'data-placeholder': _('Select one or more permissions'),\n            }),\n        )\n\n        def __init__(self, *args, **kwargs):\n            super(PermissionSetForm, self).__init__(*args, **kwargs)\n\n            # Don't display extra negative permissions field where they\n            # are not applicable\n            if language is not None:\n                del self.fields['negative_permissions']\n\n    link = lambda instance: unicode(instance.user)\n    directory_permissions = current_directory.permission_sets \\\n                                             .order_by('user').all()\n\n    return util.edit(request, template, PermissionSet, ctx, link,\n                     linkfield='user', queryset=directory_permissions,\n                     can_delete=True, form=PermissionSetForm)\n\n\nclass PermissionsUsersJSON(PootleJSONMixin, PootleAdminFormView):\n    form_class = PermissionsUsersSearchForm\n\n    @property\n    def directory(self):\n        return get_object_or_404(\n            Directory.objects,\n            pk=self.kwargs[\"directory\"])\n\n    def get_context_data(self, **kwargs):\n        context = super(\n            PermissionsUsersJSON, self).get_context_data(**kwargs)\n        form = context[\"form\"]\n        return (\n            dict(items=form.search())\n            if form.is_valid()\n            else dict(items=[]))\n\n    def get_form_kwargs(self):\n        kwargs = super(PermissionsUsersJSON, self).get_form_kwargs()\n        kwargs[\"directory\"] = self.directory\n        return kwargs\n\n    def form_valid(self, form):\n        return self.render_to_response(\n            self.get_context_data(form=form))\n\n    def form_invalid(self, form):\n        raise Http400(form.errors)\n", "path": "pootle/apps/pootle_app/views/admin/permissions.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n#\n# Copyright (C) Pootle contributors.\n#\n# This file is a part of the Pootle project. It is distributed under the GPL3\n# or later license. See the LICENSE file for a copy of the license and the\n# AUTHORS file for copyright and authorship information.\n\nfrom django import forms\nfrom django.contrib.auth import get_user_model\nfrom django.urls import reverse\n\nfrom pootle.core.decorators import get_object_or_404\nfrom pootle.core.exceptions import Http400\nfrom pootle.core.views.admin import PootleAdminFormView\nfrom pootle.core.views.mixins import PootleJSONMixin\nfrom pootle.core.views.widgets import RemoteSelectWidget\nfrom pootle.i18n.gettext import ugettext as _\nfrom pootle_app.forms import PermissionsUsersSearchForm\nfrom pootle_app.models import Directory\nfrom pootle_app.models.permissions import (PermissionSet,\n                                           get_permission_contenttype)\nfrom pootle_app.views.admin import util\nfrom pootle_misc.forms import GroupedModelChoiceField\n\n\nUser = get_user_model()\n\nPERMISSIONS = {\n    'positive': ['view', 'suggest', 'translate', 'review', 'administrate'],\n    'negative': ['hide'],\n}\n\n\nclass PermissionFormField(forms.ModelMultipleChoiceField):\n\n    def label_from_instance(self, instance):\n        return _(instance.name)\n\n\ndef admin_permissions(request, current_directory, template, ctx):\n    language = ctx.get('language', None)\n\n    negative_permissions_excl = list(PERMISSIONS['negative'])\n    positive_permissions_excl = list(PERMISSIONS['positive'])\n\n    # Don't provide means to alter access permissions under /<lang_code>/*\n    # In other words: only allow setting access permissions for the root\n    # and the `/projects/<code>/` directories\n    if language is not None:\n        access_permissions = ['view', 'hide']\n        negative_permissions_excl.extend(access_permissions)\n        positive_permissions_excl.extend(access_permissions)\n\n    content_type = get_permission_contenttype()\n\n    positive_permissions_qs = content_type.permission_set.exclude(\n        codename__in=negative_permissions_excl,\n    )\n    negative_permissions_qs = content_type.permission_set.exclude(\n        codename__in=positive_permissions_excl,\n    )\n\n    base_queryset = User.objects.filter(is_active=1).exclude(\n        id__in=current_directory.permission_sets.values_list('user_id',\n                                                             flat=True),)\n    choice_groups = [(None, base_queryset.filter(\n        username__in=('nobody', 'default')\n    ))]\n\n    choice_groups.append((\n        _('All Users'),\n        base_queryset.exclude(username__in=('nobody',\n                                            'default')).order_by('username'),\n    ))\n\n    class PermissionSetForm(forms.ModelForm):\n\n        class Meta(object):\n            model = PermissionSet\n            fields = ('user', 'directory', 'positive_permissions',\n                      'negative_permissions')\n\n        directory = forms.ModelChoiceField(\n            queryset=Directory.objects.filter(pk=current_directory.pk),\n            initial=current_directory.pk,\n            widget=forms.HiddenInput,\n        )\n        user = GroupedModelChoiceField(\n            label=_('Username'),\n            choice_groups=choice_groups,\n            queryset=User.objects.all(),\n            required=True,\n            widget=RemoteSelectWidget(\n                attrs={\n                    \"data-s2-placeholder\": _(\"Search for users to add\"),\n                    'class': ('js-select2-remote select2-username '\n                              'js-s2-new-members')}))\n        positive_permissions = PermissionFormField(\n            label=_('Add Permissions'),\n            queryset=positive_permissions_qs,\n            required=False,\n            widget=forms.SelectMultiple(attrs={\n                'class': 'js-select2 select2-multiple',\n                'data-placeholder': _('Select one or more permissions'),\n            }),\n        )\n        negative_permissions = PermissionFormField(\n            label=_('Revoke Permissions'),\n            queryset=negative_permissions_qs,\n            required=False,\n            widget=forms.SelectMultiple(attrs={\n                'class': 'js-select2 select2-multiple',\n                'data-placeholder': _('Select one or more permissions'),\n            }),\n        )\n\n        def __init__(self, *args, **kwargs):\n            super(PermissionSetForm, self).__init__(*args, **kwargs)\n\n            self.fields[\"user\"].widget.attrs[\"data-select2-url\"] = reverse(\n                \"pootle-permissions-users\",\n                kwargs=dict(directory=current_directory.pk))\n\n            # Don't display extra negative permissions field where they\n            # are not applicable\n            if language is not None:\n                del self.fields['negative_permissions']\n\n    link = lambda instance: unicode(instance.user)\n    directory_permissions = current_directory.permission_sets \\\n                                             .order_by('user').all()\n\n    return util.edit(request, template, PermissionSet, ctx, link,\n                     linkfield='user', queryset=directory_permissions,\n                     can_delete=True, form=PermissionSetForm)\n\n\nclass PermissionsUsersJSON(PootleJSONMixin, PootleAdminFormView):\n    form_class = PermissionsUsersSearchForm\n\n    @property\n    def directory(self):\n        return get_object_or_404(\n            Directory.objects,\n            pk=self.kwargs[\"directory\"])\n\n    def get_context_data(self, **kwargs):\n        context = super(\n            PermissionsUsersJSON, self).get_context_data(**kwargs)\n        form = context[\"form\"]\n        return (\n            dict(items=form.search())\n            if form.is_valid()\n            else dict(items=[]))\n\n    def get_form_kwargs(self):\n        kwargs = super(PermissionsUsersJSON, self).get_form_kwargs()\n        kwargs[\"directory\"] = self.directory\n        return kwargs\n\n    def form_valid(self, form):\n        return self.render_to_response(\n            self.get_context_data(form=form))\n\n    def form_invalid(self, form):\n        raise Http400(form.errors)\n", "path": "pootle/apps/pootle_app/views/admin/permissions.py"}]}