#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.