Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access the filtered queryset in django admin.SimpleListFilter

Say we have two objects:

class Author(models.Model):
    name = models.CharField(length=50)

class Book(models.Model):
    author = models.ForeignKey(Author)
    is_bestseller = models.BooleanField()

In BookAdmin, if we specify

list_filter = ('author', 'is_bestseller')

the choices given to you for 'author' will always be all the authors in the database, no matter whether they've written a bestseller or not.

I'd like choices of my filters to be limited by the current selection. I attempted to do this in a general manner, using admin.SimpleListFilter, however, I'm stuck, since:

model_admin.get_queryset()

returns unfiltered queryset (i.e all objects, not just those currently filtered out by the user).

How do I get the filtered queryset of the BookAdmin?

EDIT: To illustrate the problem better, here is my code:

class ForeignFieldFilter(admin.SimpleListFilter):
    field = None

    def lookups(self, request, model_admin):
        queryset = model_admin.get_queryset(request).select_related(self.field)
        # ^^^ !!! Returns UNFILTERED queryset - it's not affected by other filters

        field_ids = queryset.values_list(self.field, flat=True)

        for field_value in self.model..objects.filter(id__in=field_ids):
            yield (field_value.pk, str(field_value))

    def queryset(self, request, queryset):
        value = self.value()

        # Check that any value was passed, if not, return unmodified queryset
        if not value:
            return queryset

        return queryset.filter({'{0}__pk'.format(self.field): value})


def foreign_field_filter_factory(field, model, title=None):
    title = title or (field[:1].upper() + field[1:])
    return type(
        "ForeignFieldFilter_{0}".format(field),
        (ForeignFieldFilter,),
        {
            'field': field,
            'title': title,
            'parameter_name': field,
            'model': model
        })
like image 678
Enuy Avatar asked Feb 01 '15 00:02

Enuy


1 Answers

For BookAdmin

class AuthorBestSeller(admin.SimpleListFilter):
    title = 'Best selling authors'
    parameter_name = 'bestseller'

    def queryset(self, request, queryset):
        if self.value():
            return queryset.filter(author_id=self.value())
        else:
            return queryset

    def lookups(self, request, model_admin):
        qs = model_admin.get_queryset(request)
        query_attrs = dict([(param, val) for param, val in request.GET.items()])
        qs = qs.filter(**query_attrs)
        for book in qs.filter(is_bestseller=1):  # or might be able to use yeild here
            ret.append((book.author_id, book.author))
        return ret

edit: humm... this won't quite work either. it will just give you a list of authors with best sellers.

then add to BookAdmin

list_filter = (AuthorBestSeller, )
like image 159
warath-coder Avatar answered Oct 12 '22 10:10

warath-coder