#36228: ModelAdmin using non-default DB + RelatedOnlyFieldListFilter crashes
----------------------------+-----------------------------------------
     Reporter:  Ran Benita  |                     Type:  Bug
       Status:  new         |                Component:  contrib.admin
      Version:  5.1         |                 Severity:  Normal
     Keywords:              |             Triage Stage:  Unreviewed
    Has patch:  0           |      Needs documentation:  0
  Needs tests:  0           |  Patch needs improvement:  0
Easy pickings:  0           |                    UI/UX:  0
----------------------------+-----------------------------------------
 == Problem

 I have a `ModelAdmin` which makes all list and detail queries go to a non-
 default DB. It also uses a `RelatedOnlyFieldListFilter`.

 {{{#!python
 from django.contrib import admin

 class FooAdmin(admin.ModelAdmin):
     list_filter = (
         ('my_field', admin.RelatedOnlyFieldListFilter),
     )

     def get_queryset(self, request):
         queryset = super().get_queryset(request)
         if request.method == 'GET':
             queryset = queryset.using('other_db')
         return queryset
 }}}

 Loading the admin list page for this model crashes:

 {{{
 Traceback (most recent call last):
   [snipped]
   File "venv/lib/python3.11/site-packages/django/core/handlers/base.py",
 line 197, in _get_response
     response = wrapped_callback(request, *callback_args,
 **callback_kwargs)
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-
 packages/django/contrib/admin/options.py", line 718, in wrapper
     return self.admin_site.admin_view(view)(*args, **kwargs)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-packages/django/utils/decorators.py",
 line 188, in _view_wrapper
     result = _process_exception(request, e)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-packages/django/utils/decorators.py",
 line 186, in _view_wrapper
     response = view_func(request, *args, **kwargs)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-
 packages/django/views/decorators/cache.py", line 80, in _view_wrapper
     response = view_func(request, *args, **kwargs)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-packages/django/contrib/admin/sites.py",
 line 241, in inner
     return view(request, *args, **kwargs)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-packages/django/utils/decorators.py",
 line 48, in _wrapper
     return bound_method(*args, **kwargs)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-packages/django/utils/decorators.py",
 line 188, in _view_wrapper
     result = _process_exception(request, e)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-packages/django/utils/decorators.py",
 line 186, in _view_wrapper
     response = view_func(request, *args, **kwargs)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-
 packages/django/contrib/admin/options.py", line 2001, in changelist_view
     cl = self.get_changelist_instance(request)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-
 packages/django/contrib/admin/options.py", line 866, in
 get_changelist_instance
     return ChangeList(
            ^^^^^^^^^^^
   File "venv/lib/python3.11/site-
 packages/django/contrib/admin/views/main.py", line 145, in __init__
     self.queryset = self.get_queryset(request)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-
 packages/django/contrib/admin/views/main.py", line 545, in get_queryset
     ) = self.get_filters(request)
         ^^^^^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-
 packages/django/contrib/admin/views/main.py", line 216, in get_filters
     spec = field_list_filter_class(
            ^^^^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-
 packages/django/contrib/admin/filters.py", line 232, in __init__
     self.lookup_choices = self.field_choices(field, request, model_admin)
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-
 packages/django/contrib/admin/filters.py", line 649, in field_choices
     return field.get_choices(
            ^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-
 packages/django/db/models/fields/__init__.py", line 1076, in get_choices
     return (blank_choice if include_blank else []) + [
                                                      ^
   File "venv/lib/python3.11/site-packages/django/db/models/query.py", line
 400, in __iter__
     self._fetch_all()
   File "venv/lib/python3.11/site-packages/django/db/models/query.py", line
 1928, in _fetch_all
     self._result_cache = list(self._iterable_class(self))
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-packages/django/db/models/query.py", line
 91, in __iter__
     results = compiler.execute_sql(
               ^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-
 packages/django/db/models/sql/compiler.py", line 1561, in execute_sql
     sql, params = self.as_sql()
                   ^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-
 packages/django/db/models/sql/compiler.py", line 775, in as_sql
     self.compile(self.where) if self.where is not None else ("", [])
     ^^^^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-
 packages/django/db/models/sql/compiler.py", line 557, in compile
     sql, params = node.as_sql(self, self.connection)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-packages/django/db/models/sql/where.py",
 line 151, in as_sql
     sql, params = compiler.compile(child)
                   ^^^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-
 packages/django/db/models/sql/compiler.py", line 557, in compile
     sql, params = node.as_sql(self, self.connection)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-packages/django/db/models/lookups.py",
 line 536, in as_sql
     return super().as_sql(compiler, connection)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-packages/django/db/models/lookups.py",
 line 246, in as_sql
     rhs_sql, rhs_params = self.process_rhs(compiler, connection)
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File "venv/lib/python3.11/site-packages/django/db/models/lookups.py",
 line 502, in process_rhs
     raise ValueError(
 ValueError: Subqueries aren't allowed across different databases. Force
 the inner query to be evaluated using `list(inner_query)`.
 }}}

 I believe the problem happens because:
 - `RelatedOnlyFieldListFilter` takes the queryset from the admin, which is
 using `other_db`
 - Passes it to `Field.get_choices()` in the `limit_choices_to` parameter,
 which translates to a query lookup
 - `Field.get_choices()` runs the query against the default DB, while the
 lookup is against `other_db`

 == Proposed Solution

 - Add a `using` parameter to `Field.get_choices()`
 - Pass `using=qs.db` to `Field.get_choices()` in
 `RelatedOnlyFieldListFilter`

 If acceptable I can try to submit a PR doing this.
-- 
Ticket URL: <https://code.djangoproject.com/ticket/36228>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/django-updates/0107019561dfe590-3a61ab71-d644-40ed-8dab-980f1d2c8c95-000000%40eu-central-1.amazonses.com.

Reply via email to