Repository: openwisp/openwisp-controller (language: python)

Issue:
[feature] Create a filter using select2 and autocomplete for templates in DeviceAdmin
Similar to https://github.com/openwisp/openwisp-users/issues/270.

The list of templates available can be potentially very large, this can slow down page loading in the device admin list page in large installations.

For this reason we should look for a solution which loads the options asynchronously.

It looks like this is what we need: https://pypi.org/project/django-admin-autocomplete-filter/.


Discussion / hints:
I'm working on it.

Fix patch:
diff --git a/README.rst b/README.rst
index 85bd17336..79f789776 100644
--- a/README.rst
+++ b/README.rst
@@ -374,6 +374,7 @@ Add ``openwisp_controller`` applications to ``INSTALLED_APPS``:
         # openwisp2 admin theme
         # (must be loaded here)
         'openwisp_utils.admin_theme',
+        'admin_auto_filters',
         'django.contrib.admin',
         'django.forms',
         'import_export',
@@ -3235,6 +3236,7 @@ Now you need to add ``mycontroller.sample_config``,
     INSTALLED_APPS = [
         # other django installed apps
         'openwisp_utils.admin_theme',
+        'admin_auto_filters',
         # all-auth
         'django.contrib.sites',
         'allauth',
diff --git a/openwisp_controller/config/admin.py b/openwisp_controller/config/admin.py
index 27ebd7325..d9a81f7e8 100644
--- a/openwisp_controller/config/admin.py
+++ b/openwisp_controller/config/admin.py
@@ -28,15 +28,13 @@
     get_relevant_templates,
     get_template_default_values,
 )
-from openwisp_users.multitenancy import (
-    MultitenantOrgFilter,
-    MultitenantRelatedOrgFilter,
-)
+from openwisp_users.multitenancy import MultitenantOrgFilter
 from openwisp_utils.admin import (
     AlwaysHasChangedMixin,
     TimeReadonlyAdminMixin,
     UUIDAdmin,
 )
+from openwisp_utils.admin_theme.filters import AutocompleteFilter
 
 from ..admin import MultitenantAdminMixin
 from . import settings as app_settings
@@ -429,6 +427,19 @@ class ChangeDeviceGroupForm(forms.Form):
     )
 
 
+class TemplatesFilter(AutocompleteFilter):
+    title = _('template')
+    field_name = 'templates'
+    parameter_name = 'config__templates'
+    rel_model = Config
+
+
+class GroupFilter(AutocompleteFilter):
+    title = _('group')
+    field_name = 'group'
+    parameter_name = 'group_id__exact'
+
+
 class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin):
     recover_form_template = 'admin/config/device_recover_form.html'
     list_display = [
@@ -444,8 +455,8 @@ class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin):
     list_filter = [
         'config__status',
         ('organization', MultitenantOrgFilter),
-        ('config__templates', MultitenantRelatedOrgFilter),
-        ('group', MultitenantRelatedOrgFilter),
+        TemplatesFilter,
+        GroupFilter,
         'created',
     ]
     search_fields = [


Test patch:
diff --git a/openwisp_controller/config/tests/test_admin.py b/openwisp_controller/config/tests/test_admin.py
index 821d09fec..645645ccc 100644
--- a/openwisp_controller/config/tests/test_admin.py
+++ b/openwisp_controller/config/tests/test_admin.py
@@ -441,13 +441,25 @@ def test_recoverlist_operator_403(self):
         self._test_recoverlist_operator_403(self.app_label, 'vpn')
 
     def test_device_template_filter(self):
-        data = self._create_multitenancy_test_env()
-        t_special = self._create_template(name='special', organization=data['org1'])
-        self._test_multitenant_admin(
-            url=reverse(f'admin:{self.app_label}_device_changelist'),
-            visible=[data['t1'].name, t_special.name],
-            hidden=[data['t2'].name, data['t3_inactive'].name],
+        org = self._get_org(org_name='test-org')
+        t = self._create_template(name='test-template', organization=org)
+        d1 = self._create_device(
+            name='test-device1',
+            organization=org,
+            mac_address='00:11:22:33:44:56',
         )
+        d2 = self._create_device(
+            name='test-device2',
+            organization=org,
+            mac_address='00:11:22:33:44:57',
+        )
+        c = self._create_config(device=d1)
+        c.templates.add(t)
+        url = reverse(f'admin:{self.app_label}_device_changelist')
+        query = f'?config__templates={t.pk}'
+        response = self.client.get(f'{url}{query}')
+        self.assertContains(response, d1.name)
+        self.assertNotContains(response, d2.name)
 
     def _get_change_device_post_data(self, device):
         return {
diff --git a/tests/openwisp2/settings.py b/tests/openwisp2/settings.py
index 0276789d4..9ff30bfd2 100644
--- a/tests/openwisp2/settings.py
+++ b/tests/openwisp2/settings.py
@@ -46,6 +46,7 @@
     # openwisp2 admin theme
     # (must be loaded here)
     'openwisp_utils.admin_theme',
+    'admin_auto_filters',
     # admin
     'django.contrib.admin',
     'django.forms',