I have a simple Django model like:
class Person(models.Model):
referrer = models.ForeignKey('self', null=True)
...
In this model's ModelAdmin, how would I allow it to be filtered by whether or not referrer is null? By default, adding referrer to list_filter causes a dropdown to be shown that lists every person record, which may be in the hundreds of thousands, effectively preventing the page from loading. Even if it loads, I still can't filter by the criteria I want.
i.e. How would I modify this so that the dropdown only lists "All", "Null", or "Not Null" choices?
I've seen some posts that claim to accomplish something similar using custom FilterSpec subclasses, but none of them explain how to use them. The few I've seen appear to apply to all fields in all models, which I wouldn't want. Moreover, there's zero documentation for FilterSpec, which makes me nervous, because I don't want to invest in a lot of custom code tied to some transient internal class that might disappear by the next release.
Since Django 1.4 brings some changes to filters, I thought I save someone the time I just spent modifying the code from Cerin's accepted answer to work with Django 1.4 rc1.
I have a model that has TimeField(null=True) named "started" and I wanted to filter for null and non-null values, so it's prety much the same problem as OP.
So, here is what worked for me...
Defined (actually included) these in admin.py:
from django.contrib.admin.filters import SimpleListFilter
class NullFilterSpec(SimpleListFilter):
title = u''
parameter_name = u''
def lookups(self, request, model_admin):
return (
('1', _('Has value'), ),
('0', _('None'), ),
)
def queryset(self, request, queryset):
kwargs = {
'%s'%self.parameter_name : None,
}
if self.value() == '0':
return queryset.filter(**kwargs)
if self.value() == '1':
return queryset.exclude(**kwargs)
return queryset
class StartNullFilterSpec(NullFilterSpec):
title = u'Started'
parameter_name = u'started'
Than just used them in ModelAdmin:
class SomeModelAdmin(admin.ModelAdmin):
list_filter = (StartNullFilterSpec, )