2π
I ended up using a mixture of the top solution here, along with this snippet.
However, I had to tweak the snippet slightly, dropping the field type restriction and adding the new field_path, recently added in 1.3.
from django.contrib.admin.filterspecs import FilterSpec
from django.db import models
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _
class NullFilterSpec(FilterSpec):
#fields = (models.CharField, models.IntegerField, models.FileField)
@classmethod
def test(cls, field):
#return field.null and isinstance(field, cls.fields) and not field._choices
return field.null and not field._choices
#test = classmethod(test)
def __init__(self, f, request, params, model, model_admin, field_path=None):
super(NullFilterSpec, self).__init__(f, request, params, model, model_admin, field_path)
self.lookup_kwarg = '%s__isnull' % f.name
self.lookup_val = request.GET.get(self.lookup_kwarg, None)
def choices(self, cl):
# bool(v) must be False for IS NOT NULL and True for IS NULL, but can only be a string
for k, v in ((_('All'), None), (_('Has value'), ''), (_('Omitted'), '1')):
yield {
'selected' : self.lookup_val == v,
'query_string' : cl.get_query_string({self.lookup_kwarg : v}),
'display' : k
}
# Here, we insert the new FilterSpec at the first position, to be sure
# it gets picked up before any other
FilterSpec.filter_specs.insert(0,
# If the field has a `profilecountry_filter` attribute set to True
# the this FilterSpec will be used
(lambda f: getattr(f, 'isnull_filter', False), NullFilterSpec)
)
33π
After Django 3.1 you can use EmptyFieldListFilter:
class MyAdmin(admin.ModelAdmin):
list_filter = (
("model_field", admin.EmptyFieldListFilter),
)
- [Django]-AttributeError: can't set attribute
- [Django]-Import csv data into database in Django Admin
- [Django]-Command not found: django-admin.py
16π
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, )
- [Django]-What is a Django "app" supposed to mean?
- [Django]-No module named MySQLdb
- [Django]-Filter by property
8π
I have a simpler version of frnhrβs answer, which actually filters on __isnull
condition.
(Django 1.4+):
from django.contrib.admin import SimpleListFilter
class NullListFilter(SimpleListFilter):
def lookups(self, request, model_admin):
return (
('1', 'Null', ),
('0', '!= Null', ),
)
def queryset(self, request, queryset):
if self.value() in ('0', '1'):
kwargs = { '{0}__isnull'.format(self.parameter_name) : self.value() == '1' }
return queryset.filter(**kwargs)
return queryset
Then also:
class StartNullListFilter(NullListFilter):
title = u'Started'
parameter_name = u'started'
and finally:
class SomeModelAdmin(admin.ModelAdmin):
list_filter = (StartNullListFilter, )
I personally donβt like to trash my admin.py
with dozens of classes, so I came up with such a helper function:
def null_filter(field, title_=None):
class NullListFieldFilter(NullListFilter):
parameter_name = field
title = title_ or parameter_name
return NullListFieldFilter
Which I can later apply as in:
class OtherModelAdmin(admin.ModelAdmin):
list_filter = (null_filter('somefield'), null_filter('ugly_field', _('Beautiful Name')), )
- [Django]-How to debug Jinja2 template?
- [Django]-How to deal with this ERROR (1049, "Unknown database '/users/ohyunjun/work/astral/mysql'")
- [Django]-What does on_delete do on Django models?
6π
there is a simple way:
class RefererFilter(admin.SimpleListFilter):
title = 'has referer'
# Parameter for the filter that will be used in the URL query.
parameter_name = 'referer__isnull'
def lookups(self, request, model_admin):
return (
('False', 'has referer'),
('True', 'has no referer'),
)
def queryset(self, request, queryset):
if self.value() == 'False':
return queryset.filter(referer__isnull=False)
if self.value() == 'True':
return queryset.filter(referer__isnull=True)
Then just used them in ModelAdmin:
class PersonAdmin(admin.ModelAdmin):
list_filter = (RefererFilter,)
- [Django]-PyCharm hangs on 'scanning files to index' background task
- [Django]-Manually create a Django QuerySet or rather manually add objects to a QuerySet
- [Django]-Django dumpdata UTF-8 (Unicode)
2π
A snippet with a better explanation might be this. Django 1.4 will ship with a simplified filter mechanism.
- [Django]-How to validate a field on update in DRF?
- [Django]-How to render menu with one active item with DRY?
- [Django]-How to put comments in Django templates?
1π
There has been a ticket bouncing around for this for 4 years (https://code.djangoproject.com/ticket/5833). It missed the 1.3 milestone, but has reached new feature status and presumably has found itβs way into trunk. If you donβt mind running off of trunk, you can use it now. The patch is supposedly 1.3 compatible, though, so you can probably get by with just patching your currently installation.
- [Django]-Securing communication [Authenticity, Privacy & Integrity] with mobile app?
- [Django]-Disabled field is not passed through β workaround needed
- [Django]-Django β why is the request.POST object immutable?
0π
For, django versions less that 3.1, copy the code of EmptyFieldListFilter
below
from django.contrib.admin import FieldListFilter
from django.contrib.admin.options import IncorrectLookupParameters
from django.core.exceptions import ImproperlyConfigured
from django.db import models
from django.utils.translation import gettext_lazy as _
class EmptyFieldListFilter(FieldListFilter):
def __init__(self, field, request, params, model, model_admin, field_path):
if not field.empty_strings_allowed and not field.null:
raise ImproperlyConfigured(
"The list filter '%s' cannot be used with field '%s' which "
"doesn't allow empty strings and nulls." % (
self.__class__.__name__,
field.name,
)
)
self.lookup_kwarg = '%s__isempty' % field_path
self.lookup_val = params.get(self.lookup_kwarg)
super().__init__(field, request, params, model, model_admin, field_path)
def queryset(self, request, queryset):
if self.lookup_kwarg not in self.used_parameters:
return queryset
if self.lookup_val not in ('0', '1'):
raise IncorrectLookupParameters
lookup_condition = models.Q()
if self.field.empty_strings_allowed:
lookup_condition |= models.Q(**{self.field_path: ''})
if self.field.null:
lookup_condition |= models.Q(**{'%s__isnull' % self.field_path: True})
if self.lookup_val == '1':
return queryset.filter(lookup_condition)
return queryset.exclude(lookup_condition)
def expected_parameters(self):
return [self.lookup_kwarg]
def choices(self, changelist):
for lookup, title in (
(None, _('All')),
('1', _('Empty')),
('0', _('Not empty')),
):
yield {
'selected': self.lookup_val == lookup,
'query_string': changelist.get_query_string({self.lookup_kwarg: lookup}),
'display': title,
}
You can use this to define null field value filter in admin as
import myfile
class MyAdmin(admin.ModelAdmin):
list_filter = (
("model_field", myfile.EmptyFieldListFilter),
)
- [Django]-How to add the custom button which executes a Django admin action to change form page?
- [Django]-Permission denied β nginx and uwsgi socket
- [Django]-Django Password Generator