Repository: allegro/ralph

Issue:
Error 500 by search a Parent
Hi
I become an 500 error when i will search a Parent by hardware under Data center (When I click on the magnifying glass).

Here is the code:

```
AssertionError at /data_center/datacenterasset/
Cannot combine a unique query with a non-unique query.
Request Method: GET
Request URL:    http://myip/data_center/datacenterasset/?_to_field=id&_popup=1
Django Version: 1.8.9
Exception Type: AssertionError
Exception Value:    
Cannot combine a unique query with a non-unique query.
Exception Location: /opt/ralph/ralph-core/lib/python3.4/site-packages/django/db/models/sql/query.py in combine, line 500
Python Executable:  /opt/ralph/ralph-core/bin/python
Python Version: 3.4.3
Python Path:    
['/opt/ralph/ralph-core/bin',
 '/opt/ralph/ralph-core/lib/python3.4',
 '/opt/ralph/ralph-core/lib/python3.4/plat-x86_64-linux-gnu',
 '/opt/ralph/ralph-core/lib/python3.4/lib-dynload',
 '/usr/lib/python3.4',
 '/usr/lib/python3.4/plat-x86_64-linux-gnu',
 '/opt/ralph/ralph-core/lib/python3.4/site-packages']
Server time:    Fri, 26 Feb 2016 10:00:06 +0100

Environment:


Request Method: GET
Request URL: http://myip/data_center/datacenterasset/?_to_field=id&_popup=1

Django Version: 1.8.9
Python Version: 3.4.3
Installed Applications:
('ralph.admin',
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.humanize',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'import_export',
 'mptt',
 'reversion',
 'sitetree',
 'ralph.accounts',
 'ralph.assets',
 'ralph.attachments',
 'ralph.back_office',
 'ralph.data_center',
 'ralph.licences',
 'ralph.domains',
 'ralph.supports',
 'ralph.lib.foundation',
 'ralph.lib.table',
 'ralph.data_importer',
 'ralph.dc_view',
 'ralph.reports',
 'ralph.lib.transitions',
 'ralph.lib.permissions',
 'rest_framework',
 'rest_framework.authtoken',
 'taggit',
 'taggit_serializer')
Installed Middleware:
('django.middleware.common.CommonMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'django.middleware.security.SecurityMiddleware')


Traceback:
File "/opt/ralph/ralph-core/lib/python3.4/site-packages/django/core/handlers/base.py" in get_response
  132.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/usr/lib/python3.4/contextlib.py" in inner
  30.                 return func(*args, **kwds)
File "/opt/ralph/ralph-core/lib/python3.4/site-packages/django/contrib/admin/options.py" in wrapper
  618.                 return self.admin_site.admin_view(view)(*args, **kwargs)
File "/opt/ralph/ralph-core/lib/python3.4/site-packages/django/utils/decorators.py" in _wrapped_view
  110.                     response = view_func(request, *args, **kwargs)
File "/opt/ralph/ralph-core/lib/python3.4/site-packages/django/views/decorators/cache.py" in _wrapped_view_func
  57.         response = view_func(request, *args, **kwargs)
File "/opt/ralph/ralph-core/lib/python3.4/site-packages/django/contrib/admin/sites.py" in inner
  233.             return view(request, *args, **kwargs)
File "/opt/ralph/ralph-core/lib/python3.4/site-packages/reversion/revisions.py" in do_revision_context
  297.                 return func(*args, **kwargs)
File "/opt/ralph/ralph-core/lib/python3.4/site-packages/ralph/admin/mixins.py" in changelist_view
  187.             request, extra_context
File "/opt/ralph/ralph-core/lib/python3.4/site-packages/reversion/admin.py" in changelist_view
  419.         return super(VersionAdmin, self).changelist_view(request, context)
File "/opt/ralph/ralph-core/lib/python3.4/site-packages/django/utils/decorators.py" in _wrapper
  34.             return bound_func(*args, **kwargs)
File "/opt/ralph/ralph-core/lib/python3.4/site-packages/django/utils/decorators.py" in _wrapped_view
  110.                     response = view_func(request, *args, **kwargs)
File "/opt/ralph/ralph-core/lib/python3.4/site-packages/django/utils/decorators.py" in bound_func
  30.                 return func.__get__(self, type(self))(*args2, **kwargs2)
File "/opt/ralph/ralph-core/lib/python3.4/site-packages/django/contrib/admin/options.py" in changelist_view
  1550.                 self.list_max_show_all, self.list_editable, self)
File "/opt/ralph/ralph-core/lib/python3.4/site-packages/ralph/admin/views/main.py" in __init__
  14.         super().__init__(request, *args, **kwargs)
File "/opt/ralph/ralph-core/lib/python3.4/site-packages/django/contrib/admin/views/main.py" in __init__
  81.         self.queryset = self.get_queryset(request)
File "/opt/ralph/ralph-core/lib/python3.4/site-packages/ralph/admin/views/main.py" in get_queryset
  66.                 queryset = queryset & autocomplete_queryset()
File "/opt/ralph/ralph-core/lib/python3.4/site-packages/django/db/models/query.py" in __and__
  211.         combined.query.combine(other.query, sql.AND)
File "/opt/ralph/ralph-core/lib/python3.4/site-packages/django/db/models/sql/query.py" in combine
  500.             "Cannot combine a unique query with a non-unique query."

Exception Type: AssertionError at /data_center/datacenterasset/
Exception Value: Cannot combine a unique query with a non-unique query.
```

Thanks for help.



Discussion:

Resolving patch:
diff --git a/src/ralph/admin/views/main.py b/src/ralph/admin/views/main.py
--- a/src/ralph/admin/views/main.py
+++ b/src/ralph/admin/views/main.py
@@ -63,5 +63,18 @@ def get_queryset(self, request):
                 self.model, 'get_autocomplete_queryset', None
             )
             if autocomplete_queryset:
-                queryset = queryset & autocomplete_queryset()
+                autocomplete_queryset = autocomplete_queryset()
+                # #2248 - cannot combine unique (distinct) and non-unique query
+                # if one of the queries is distinct, make sure all are distinct
+                if (
+                    queryset.query.distinct and
+                    not autocomplete_queryset.query.distinct
+                ):
+                    autocomplete_queryset = autocomplete_queryset.distinct()
+                if (
+                    not queryset.query.distinct and
+                    autocomplete_queryset.query.distinct
+                ):
+                    queryset = queryset.distinct()
+                queryset = queryset & autocomplete_queryset
         return queryset


Test patch:
diff --git a/src/ralph/api/tests/test_viewsets.py b/src/ralph/api/tests/test_viewsets.py
--- a/src/ralph/api/tests/test_viewsets.py
+++ b/src/ralph/api/tests/test_viewsets.py
@@ -191,5 +191,6 @@ class TestAdminSearchFieldsMixin(RalphTestCase):
     def test_get_filter_fields_from_admin(self):
         cvs = CarViewSet()
         self.assertEqual(
-            cvs.filter_fields, ['manufacturer__name', 'name', 'year']
+            cvs.filter_fields,
+            ['manufacturer__name', 'name', 'foos__bar', 'year']
         )
diff --git a/src/ralph/tests/admin.py b/src/ralph/tests/admin.py
--- a/src/ralph/tests/admin.py
+++ b/src/ralph/tests/admin.py
@@ -9,7 +9,7 @@
 class CarAdmin(RalphAdmin):
     ordering = ['name']
     list_filter = ['year']
-    search_fields = ['name']
+    search_fields = ['name', 'foos__bar']
 
 
 @register(Bar)
diff --git a/src/ralph/tests/migrations/0004_car_foos.py b/src/ralph/tests/migrations/0004_car_foos.py
new file mode 100644
--- /dev/null
+++ b/src/ralph/tests/migrations/0004_car_foos.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('tests', '0003_bar'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='car',
+            name='foos',
+            field=models.ManyToManyField(to='tests.Foo'),
+        ),
+    ]
diff --git a/src/ralph/tests/models.py b/src/ralph/tests/models.py
--- a/src/ralph/tests/models.py
+++ b/src/ralph/tests/models.py
@@ -38,6 +38,7 @@ class Car(models.Model):
     manufacturer = models.ForeignKey(Manufacturer)
     manufacturer._autocomplete = False
     manufacturer._filter_title = 'test'
+    foos = models.ManyToManyField(Foo)
 
     @classmethod
     def get_autocomplete_queryset(cls):