15👍
I eventually solved it by browsing the source code which can be found [here in this link][1] and overriding the choices
function. I only changed the "All" selection to show "Yes" in this code that was added to my filter class :
from django.utils.encoding import force_text
def choices(self, changelist):
"""Copied from source code to remove the "All" Option"""
yield {
'selected': self.value() is None,
'query_string': changelist.get_query_string({}, [self.parameter_name]),
'display': 'Yes',
}
for lookup, title in self.lookup_choices:
yield {
'selected': self.value() == force_text(lookup),
'query_string': changelist.get_query_string({self.parameter_name: lookup}, []),
'display': title,
}
As Linh mentioned: For anyone only want to remove the all option just delete 'display': 'Yes'
, from the first yield{} in choices function
[1]: https://github.com/django/django/blob/main/django/contrib/admin/filters.py#L44
1👍
Perhaps even cleaner, if you’re looking to remove the All option entirely, is simply to pop off (consume) the first item from the generator. The yield
in the source code you found in the Django source is a python keyword that means ‘each time you ask for a new value, return the next one, until we run out’
It makes it possible to have a big list of things but only load one into memory at a time and some other neat side benefits. You trigger the consume behavior when you add the generator into a loop, but you can do it one at a time with the next
function.
So this would be a really nice way to pop off the ‘All’ while still working with any changes Django makes in the future:
class DealExpiredListFilter(admin.SimpleListFilter):
def choices(self, changelist):
choices = super().choices(changelist)
next(choices)
return choices
- Django: migration x in app x has no Migration class
- Django ORM leaves idle connections on Postgres DB
- How to use email instead of username for user authentication?
- Encoding gives "'ascii' codec can't encode character … ordinal not in range(128)"
- Is it possible to disable django related_name for a specific field?
0👍
You can copy, paste and override choices() then uncomment "display": _("All"),
as shown below to remove All
option from Django Admin filter:
class DealExpiredListFilter(admin.SimpleListFilter):
def choices(self, changelist): # Here
yield {
"selected": self.value() is None,
"query_string": changelist.get_query_string(remove=[self.parameter_name]),
# "display": _("All"), # Here
}
for lookup, title in self.lookup_choices:
yield {
"selected": self.value() == str(lookup),
"query_string": changelist.get_query_string(
{self.parameter_name: lookup}
),
"display": title,
}
0👍
All or Nothing?
This is a solution based on the above answers, but using Django 4.1. Django 4.1 implements a Clear all filters
button (#27888 closed Cleanup/optimization (fixed) Add a button to clear all admin filters) which appears after the admin user has made a filter selection, and when pressed essentially provides the All
option by the back door as it were. In addition there is the question of what the page should display before the admin user has made a filter selection at all – again this is the same as the All
option. In my implementation it makes no sense to be able to view all the records (races) for all events (the fk filter) together in a huge list, I only want to be able to view all the races for a single event at a time. When you provide a custom list filter class you are required to implement lookups
and queryset
functions as described by the op. Here are mine:
class NoAllListFilter(admin.SimpleListFilter):
title = 'event'
parameter_name = 'event__name'
def lookups(self, request, model_admin):
return (
tuple((event.name, event.name) for event in Event.objects.all())
)
def queryset(self, request, queryset):
return queryset.filter(event__name = self.value())
My queryset
function is different to the documented example in ModelAdmin List Filters
in that it does not check self.value()
first, so when no event selection has been made, or Clear all filters
is pressed, it provides a queryset which doesn’t match any records at all, which is actually what I want.
By contrast, this modified queryset
function, which is more like the documented example, but which does not suit my purpose, produces a queryset which displays all records (all races in all events) when no event selection has been made, or when Clear all filters
is pressed (counter-example only – not used):
def queryset(self, request, queryset):
if self.value():
return queryset.filter(event__name = self.value())
Returning to my first, implemented, queryset
function (not the counter example), to align with the 0 races
displayed when no event selection has been made, or when Clear all filters
is pressed, instead of commenting out the initially generated choices
yield, I changed the display from All
to None
. Logically Clear all filters
really, and in most cases, means don’t filter anything and display all records, but in this instance I think the None
option replacing the All
makes it clear that no records are expected to be displayed. Here is my modified Django 4.1 choices
function in the NoAllListFilter
class:
def choices(self, changelist):
# Modified from Django 4.1 source
yield {
"selected": self.value() is None,
"query_string": changelist.get_query_string(remove=[self.parameter_name]),
# "display": _("All"),
"display": _("None"),
}
for lookup, title in self.lookup_choices:
yield {
"selected": self.value() == str(lookup),
"query_string": changelist.get_query_string(
{self.parameter_name: lookup}
),
"display": title,
}
Note that the comment that the method force_text
has been deprecated from Django 4.0 onwards is doubtless correct but no longer applicable because the Django 4.1 choices
function just uses str(lookup)
rather than force_text(lookup)
- Where should you update Celery settings? On the remote worker or sender?
- How can I insert parameters in raw SQL in Django Python
- RexProScriptException transaction is not open in Django with Titan (graph DB)
- Tiny MCE popups blank in Django admin