Opened 2 years ago

Closed 2 years ago

#23055 closed Bug (fixed)

Filters don't use ModelAdmin get_queryset()

Reported by: ramiro Owned by: ramiro
Component: contrib.admin Version: master
Severity: Normal Keywords: admin filters list_filter multi-db get_queryset modeladmin
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by ramiro)

If one is using a ModelAdmin with a custom get_queryset() method (e.g. as described in 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/ b/django/contrib/admin/
index d5f31ab..d04d5cc 100644
--- a/django/contrib/admin/
+++ b/django/contrib/admin/
@@ -361,7 +361,7 @@ class AllValuesFieldListFilter(FieldListFilter):
         self.lookup_val_isnull = request.GET.get(self.lookup_kwarg_isnull,
         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.

Change History (12)

comment:1 Changed 2 years ago by ramiro

  • Description modified (diff)

comment:2 Changed 2 years ago by timo

  • Triage Stage changed from Unreviewed to Accepted

comment:3 follow-up: Changed 2 years ago 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

    '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

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.

comment:4 in reply to: ↑ 3 Changed 2 years ago by ramiro

Replying to 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.

comment:5 Changed 2 years ago by Ola Sitarska <ola@…>

  • Owner changed from nobody to anonymous
  • Status changed from new to assigned

comment:6 Changed 2 years ago by olasitarska

  • Owner changed from anonymous to olasitarska

Sorry, I didn't login :)

comment:7 Changed 2 years ago by olasitarska

Ok, so I wrote a test for that:

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?

comment:8 Changed 2 years ago 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.

comment:9 Changed 2 years ago by olasitarska

  • Owner olasitarska deleted
  • Status changed from assigned to new

comment:10 Changed 2 years ago by ramiro

  • Has patch set
  • Owner set to ramiro
  • Status changed from new to assigned

comment:11 Changed 2 years ago by timgraham

  • Triage Stage changed from Accepted to Ready for checkin

Minor test failure on non-sqlite; looks good otherwise.

comment:12 Changed 2 years ago by Ramiro Morales <cramm0@…>

  • Resolution set to fixed
  • Status changed from assigned to closed

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

Note: See TracTickets for help on using tickets.
Back to Top