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

Issue:
[bug] Using autocomplete filter with malformed primary key raises 500 error 
**Steps to replicate**

1. Start  the development server 
2. Visit http://127.0.0.1:8000/admin/test_project/shelf/?owner_id=s 

**Actual outcome**

`ValueError at /admin/test_project/shelf/` is raised because the entered value for `owner_id` parameter is not of the same type as primary key of the Owner model 

**Expected outcome**

The autocomplete filter should verify that the type of parameter value is same as type of primary key of the model before performing database query. If parameter is malformed (different type than of the primary key), then it should take the user back to the changelist page. 

**Possible solution**

We can override the `value()` method of the `AutocompleteFilter` to check  the type of the input value. E.g. for UUID values the code would look like this: 

```python
def value(self):
        value = super().value()
        if value is None:
            return value
        try:
            uuid.UUID(value)
        except ValueError as error:
            raise IncorrectLookupParameters(error)
        else:
            return value
```

We have to make this method smart to automatically know the type of remote field's primary key. 




Discussion / hints:

Fix patch:
diff --git a/openwisp_utils/admin_theme/filters.py b/openwisp_utils/admin_theme/filters.py
index d3e3adba..7f25dc0f 100644
--- a/openwisp_utils/admin_theme/filters.py
+++ b/openwisp_utils/admin_theme/filters.py
@@ -1,7 +1,8 @@
 from admin_auto_filters.filters import AutocompleteFilter as BaseAutocompleteFilter
+from django.contrib import messages
 from django.contrib.admin.filters import FieldListFilter, SimpleListFilter
 from django.contrib.admin.utils import NotRelationField, get_model_from_relation
-from django.core.exceptions import ImproperlyConfigured
+from django.core.exceptions import ImproperlyConfigured, ValidationError
 from django.db.models.fields import CharField, UUIDField
 from django.urls import reverse
 from django.utils.translation import gettext_lazy as _
@@ -103,3 +104,17 @@ class Media:
 
     def get_autocomplete_url(self, request, model_admin):
         return reverse('admin:ow-auto-filter')
+
+    def __init__(self, *args, **kwargs):
+        try:
+            return super().__init__(*args, **kwargs)
+        except ValidationError:
+            None
+
+    def queryset(self, request, queryset):
+        try:
+            return super().queryset(request, queryset)
+        except ValidationError as e:
+            error_msg = ' '.join(e.messages)
+            messages.error(request, error_msg)
+            return queryset


Test patch:
diff --git a/tests/test_project/tests/test_admin.py b/tests/test_project/tests/test_admin.py
index d613671d..740cefea 100644
--- a/tests/test_project/tests/test_admin.py
+++ b/tests/test_project/tests/test_admin.py
@@ -429,3 +429,10 @@ def test_ow_auto_filter_view_reverse_relation(self):
         url = f'{url}?app_label=test_project&model_name=shelf&field_name=book'
         response = self.client.get(url)
         self.assertEqual(response.status_code, 200)
+
+    def test_ow_autocomplete_filter_uuid_exception(self):
+        url = reverse('admin:test_project_book_changelist')
+        url = f'{url}?shelf__id=invalid'
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
+        self.assertContains(response, '“invalid” is not a valid UUID.')