Implement auto-complete for /admin/ forms that reference User
authorMagnus Hagander <magnus@hagander.net>
Thu, 23 Jun 2016 15:57:38 +0000 (17:57 +0200)
committerMagnus Hagander <magnus@hagander.net>
Thu, 23 Jun 2016 15:59:45 +0000 (17:59 +0200)
We have so many users now that loading these forms take forever.
Instead, implement a textbox with autocomplete using django-selectable,
which will not load the whole list of users at once.

pgweb/contributors/admin.py
pgweb/core/admin.py
pgweb/core/lookups.py [new file with mode: 0644]
pgweb/settings.py
pgweb/urls.py
templates/admin/base_site.html

index f18240534b6e4b16b1274a1d0e68a6a97de8dfca..9f2204e7748b585209ab8b5adac4a7446439ed90 100644 (file)
@@ -1,5 +1,27 @@
+from django import forms
 from django.contrib import admin
+
+from selectable.forms.widgets import AutoCompleteSelectWidget
+
+from pgweb.core.lookups import UserLookup
+
 from models import Contributor, ContributorType
 
+class ContributorAdminForm(forms.ModelForm):
+       class Meta:
+               model = Contributor
+               exclude = ()
+               widgets = {
+                       'user': AutoCompleteSelectWidget(lookup_class=UserLookup),
+               }
+
+       def __init__(self, *args, **kwargs):
+               super(ContributorAdminForm, self).__init__(*args, **kwargs)
+               self.fields['user'].widget.can_add_related = False
+               self.fields['user'].widget.can_change_related = False
+
+class ContributorAdmin(admin.ModelAdmin):
+       form = ContributorAdminForm
+
 admin.site.register(ContributorType)
-admin.site.register(Contributor)
+admin.site.register(Contributor, ContributorAdmin)
index a010b143a959e2d30e7f73a0d72e09cf61c34f0e..40cff33239eec4e2ab600ad6abfc3c012206278e 100644 (file)
@@ -1,14 +1,33 @@
+from django import forms
 from django.contrib import admin
 
+from selectable.forms.widgets import AutoCompleteSelectMultipleWidget
+
 from pgweb.core.models import Version, OrganisationType, Organisation
 from pgweb.core.models import ImportedRSSFeed, ImportedRSSItem
 from pgweb.core.models import ModerationNotification
 
+from pgweb.core.lookups import UserLookup
+
+class OrganisationAdminForm(forms.ModelForm):
+       class Meta:
+               model = Organisation
+               exclude = ()
+               widgets = {
+                       'managers': AutoCompleteSelectMultipleWidget(lookup_class=UserLookup),
+               }
+
+       def __init__(self, *args, **kwargs):
+               super(OrganisationAdminForm, self).__init__(*args, **kwargs)
+               self.fields['managers'].widget.can_add_related = False
+               self.fields['managers'].widget.can_change_related = False
+               self.fields['managers'].widget.can_delete_related = False
+
 class OrganisationAdmin(admin.ModelAdmin):
+       form = OrganisationAdminForm
        list_display = ('name', 'approved', 'lastconfirmed',)
        list_filter = ('approved',)
        ordering = ('name', )
-       filter_horizontal = ('managers', )
        search_fields = ('name', )
 
 class VersionAdmin(admin.ModelAdmin):
diff --git a/pgweb/core/lookups.py b/pgweb/core/lookups.py
new file mode 100644 (file)
index 0000000..806d1d8
--- /dev/null
@@ -0,0 +1,25 @@
+from django.contrib.auth.models import User
+from selectable.base import ModelLookup
+from selectable.registry import registry
+from selectable.decorators import staff_member_required
+
+
+@staff_member_required
+class UserLookup(ModelLookup):
+       model = User
+       search_fields = (
+               'username__icontains',
+               'first_name__icontains',
+               'last_name__icontains',
+       )
+       filters = {'is_active': True, }
+
+       def get_item_value(self, item):
+               # Display for currently selected item
+               return u"%s (%s)" % (item.username, item.get_full_name())
+
+       def get_item_label(self, item):
+               # Display for choice listings
+               return u"%s (%s)" % (item.username, item.get_full_name())
+
+registry.register(UserLookup)
index e43c3b6b35d7264664c5f57a793f07f42bca7924..c31cf40b84b66642d40e24579899210758682fd7 100644 (file)
@@ -98,6 +98,7 @@ INSTALLED_APPS = [
     'django.contrib.admin',
     'django_markwhat',
        'django.contrib.staticfiles',
+    'pgweb.selectable',
     'pgweb.core',
     'pgweb.mailqueue',
     'pgweb.account',
index e73f4eaa431598b88d3b635e18ec7a0317616b54..e439f4c91c4111a38241dc65a27866144c7a898a 100644 (file)
@@ -135,6 +135,9 @@ urlpatterns = patterns('',
        # Uncomment the next line to enable the admin:
        (r'^admin/', include(admin.site.urls)),
 
+    # We use selectable...
+       (r'^selectable/', include('selectable.urls')),
+
        # This should not happen in production - serve by the webserver natively!
        url(r'^(favicon.ico)$', 'django.views.static.serve', {
                'document_root': 'media',
index ed75f40d8b932cd168c1b6308563c70d46e47f42..78cdfa2d55c76c01125117436017df61484f50fa 100644 (file)
@@ -1,2 +1,16 @@
 {%extends "admin/base.html"%}
 {%block branding%}Welcome to the PostgreSQL website administration site | <b><a href="/admin/pending/">Pending</a></b> moderation | <b><a href="/admin/purge/">Purge</a></b> from frontend{%endblock%}
+
+{%block extrahead%}
+<link rel="stylesheet" href="/media/css/jquery-ui.css" type="text/css">
+<link href="/media/selectable/css/dj.selectable.css" type="text/css" media="all" rel="stylesheet">
+<script src="/media/js/jquery.min.js"></script>
+<script src="/media/js/jquery-ui.min.js"></script>
+<script type="text/javascript" src="/media/selectable/js/jquery.dj.selectable.js"></script>
+<style>
+ul.selectable-deck li.selectable-deck-item a.selectable-deck-remove {
+   float: none;
+   margin-left: 10px;
+}
+</style>
+{%endblock%}