But when usage of the `list_filter` feature is added, a DB error is
generated reporting that no table object for the model a hand is found in
the default Django DB.
This is because the filter machinery doesn't use the custom ModelAdmin-
dictated !QuerySet but the model's default manager `all()` method:
{{{
queryset = parent_model._default_manager.all()
}}}
Replacing it with:
{{{
diff --git a/django/contrib/admin/filters.py
b/django/contrib/admin/filters.py
index d5f31ab..d04d5cc 100644
--- a/django/contrib/admin/filters.py
+++ b/django/contrib/admin/filters.py
@@ -361,7 +361,7 @@ class AllValuesFieldListFilter(FieldListFilter):
self.lookup_val_isnull =
request.GET.get(self.lookup_kwarg_isnull,
None)
parent_model, reverse_path = reverse_field_path(model,
field_path)
- queryset = parent_model._default_manager.all()
+ queryset = model_admin.get_queryset(request)
# optional feature: limit choices base on existing relationships
# queryset = queryset.complex_filter(
# {'%s__isnull' % reverse_path: False})
}}}
solves the problem.
--
Ticket URL: <https://code.djangoproject.com/ticket/23055>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
Old description:
New description:
If one is using a ModelAdmin with a custom `get_queryset()` method (e.g.
as described in https://docs.djangoproject.com/en/1.7/topics/db/multi-db
/#exposing-multiple-databases-in-django-s-admin-interface to handle
routing of models hosted in a multi-DB setup), displaying the change list
works as expected.
But when usage of the `list_filter` feature is added, a DB error is
generated reporting that no table object for the model at hand is found in
the default Django DB.
This is because the filter machinery doesn't use the custom ModelAdmin-
dictated !QuerySet but the model's default manager `all()` method:
{{{
queryset = parent_model._default_manager.all()
}}}
Replacing it with:
{{{
diff --git a/django/contrib/admin/filters.py
b/django/contrib/admin/filters.py
index d5f31ab..d04d5cc 100644
--- a/django/contrib/admin/filters.py
+++ b/django/contrib/admin/filters.py
@@ -361,7 +361,7 @@ class AllValuesFieldListFilter(FieldListFilter):
self.lookup_val_isnull =
request.GET.get(self.lookup_kwarg_isnull,
None)
parent_model, reverse_path = reverse_field_path(model,
field_path)
- queryset = parent_model._default_manager.all()
+ queryset = model_admin.get_queryset(request)
# optional feature: limit choices base on existing relationships
# queryset = queryset.complex_filter(
# {'%s__isnull' % reverse_path: False})
}}}
solves the problem.
--
--
Ticket URL: <https://code.djangoproject.com/ticket/23055#comment:1>
* stage: Unreviewed => Accepted
--
Ticket URL: <https://code.djangoproject.com/ticket/23055#comment:2>
Comment (by synasius):
Hi ramiro,
I'm trying to reproduce the issue but I can't. Can you help me?
This is my current setup: I have a `foo` application with just one `Stuff`
model
{{{
#!python
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
},
'other': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'other.sqlite3'),
},
}
DATABASE_ROUTERS = ['help.routers.FooRouter']
}}}
and that's my ModelAdmin
{{{
#!python
class MultiDBModelAdmin(admin.ModelAdmin):
# A handy constant for the name of the alternate database.
using = 'other'
def get_queryset(self, request):
# Tell Django to look for objects on the 'other' database.
return super(MultiDBModelAdmin,
self).get_queryset(request).using(self.using)
# Register your models here.
class StuffAdmin(MultiDBModelAdmin):
list_filter = ("empty",)
}}}
The router is used to create the `foo_stuff` table only in the `other` db.
--
Ticket URL: <https://code.djangoproject.com/ticket/23055#comment:3>
Comment (by ramiro):
Replying to [comment:3 synasius]:
> Hi ramiro,
>
> I'm trying to reproduce the issue but I can't. Can you help me?
No router should be used (or rather, no custom DB routing scheme should be
in place, just use the default setup). That's why I didn't mention them in
the report.
This setup could well be a corner case but in practice there is no need
to use multi-DB to experiece the bug:
Imagine you use just one database, and you limit the set of available
objects that can be handled in the admin by returning a filtered queryset
in the relevant !ModelAdmin `get_queryset()` method and so the changelist
will list only the model instances resulting from such filtering. But
because of this bug, the right-side filters will be created based on the
full unfiltered queryset of the model's default manager.
--
Ticket URL: <https://code.djangoproject.com/ticket/23055#comment:4>
* status: new => assigned
* owner: nobody => anonymous
--
Ticket URL: <https://code.djangoproject.com/ticket/23055#comment:5>
* owner: anonymous => olasitarska
Comment:
Sorry, I didn't login :)
--
Ticket URL: <https://code.djangoproject.com/ticket/23055#comment:6>
Comment (by olasitarska):
Ok, so I wrote a test for that:
https://github.com/olasitarska/django/commit/b36252346b78f5a387133bba2b889ed9e253edfe
And when I tried implementing solution proposed here by ramiro, it is
breaking another test.
{{{
ERROR: test_fieldlistfilter_underscorelookup_tuple
(admin_filters.tests.ListFiltersTests)
----------------------------------------------------------------------
FieldError: Cannot resolve keyword 'email' into field. Choices are:
author, author_id, contributors, date_registered, id, is_best_seller, no,
title, year
}}}
It happens because in scenario when we are filtering by `author__email`,
this line:
{{{
queryset = parent_model._default_manager.all()
}}}
would return a User Queryset, while this line:
{{{
queryset = model_admin.get_queryset(request)
}}}
still returns a Book Queryset.
I am totally not an expert (it's actually my first contribution). We can
probably check if model connected to `modeladmin` is the same as `model`
and act accordingly, but how can we pass along those filters from custom
modeladmin queryset?
--
Ticket URL: <https://code.djangoproject.com/ticket/23055#comment:7>
Comment (by olasitarska):
I dug some more into the code and I think that issue ramiro is referring
to in his last comment is actually somewhere else - filters like that are
rendered by class RelatedFieldListFilter which returns choices directly
from Model field.
--
Ticket URL: <https://code.djangoproject.com/ticket/23055#comment:8>
* status: assigned => new
* owner: olasitarska =>
--
Ticket URL: <https://code.djangoproject.com/ticket/23055#comment:9>
* owner: => ramiro
* status: new => assigned
* has_patch: 0 => 1
Comment:
PR is at https://github.com/django/django/pull/3136
--
Ticket URL: <https://code.djangoproject.com/ticket/23055#comment:10>
* stage: Accepted => Ready for checkin
Comment:
Minor test failure on non-sqlite; looks good otherwise.
--
Ticket URL: <https://code.djangoproject.com/ticket/23055#comment:11>
* status: assigned => closed
* resolution: => fixed
Comment:
In [changeset:"3a297d78169b7d3c6d688cf0e349f4663acc6bad"]:
{{{
#!CommitTicketReference repository=""
revision="3a297d78169b7d3c6d688cf0e349f4663acc6bad"
Fixed #23055 -- Made generic admin filter obey ModelAdmin queryset.
Thanks to Trac user synasius and to Ola Sitarska for helping in
identifying the issue and helping with the test case.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/23055#comment:12>