Index: contrib/admin/filterspecs.py
===================================================================
--- contrib/admin/filterspecs.py	(revision 9084)
+++ contrib/admin/filterspecs.py	(working copy)
@@ -59,7 +59,9 @@
         else:
             self.lookup_title = f.verbose_name
         self.lookup_kwarg = '%s__%s__exact' % (f.name, f.rel.to._meta.pk.name)
+        self.lookup_null_kwarg = '%s__isnull' % (f.name)
         self.lookup_val = request.GET.get(self.lookup_kwarg, None)
+        self.lookup_null = request.GET.get(self.lookup_null_kwarg, None)
         self.lookup_choices = f.rel.to._default_manager.all()
 
     def has_output(self):
@@ -69,14 +71,18 @@
         return self.lookup_title
 
     def choices(self, cl):
-        yield {'selected': self.lookup_val is None,
-               'query_string': cl.get_query_string({}, [self.lookup_kwarg]),
+        yield {'selected': self.lookup_val is None and not self.lookup_null,
+               'query_string': cl.get_query_string({}, [self.lookup_kwarg, self.lookup_null_kwarg]),
                'display': _('All')}
         for val in self.lookup_choices:
             pk_val = getattr(val, self.field.rel.to._meta.pk.attname)
             yield {'selected': self.lookup_val == smart_unicode(pk_val),
-                   'query_string': cl.get_query_string({self.lookup_kwarg: pk_val}),
+                   'query_string': cl.get_query_string({self.lookup_kwarg: pk_val}, [self.lookup_null_kwarg]),
                    'display': val}
+        if self.field.null:
+            yield {'selected': self.lookup_null,
+                   'query_string': cl.get_query_string({self.lookup_null_kwarg: 'True'}, [self.lookup_kwarg]),
+                   'display': 'Is null'}
 
 FilterSpec.register(lambda f: bool(f.rel), RelatedFilterSpec)
 
@@ -162,18 +168,24 @@
     def __init__(self, f, request, params, model, model_admin):
         super(AllValuesFilterSpec, self).__init__(f, request, params, model, model_admin)
         self.lookup_val = request.GET.get(f.name, None)
+        self.lookup_null = request.GET.get(f.name + '__isnull', None)
         self.lookup_choices = model_admin.queryset(request).distinct().order_by(f.name).values(f.name)
 
     def title(self):
         return self.field.verbose_name
 
     def choices(self, cl):
-        yield {'selected': self.lookup_val is None,
-               'query_string': cl.get_query_string({}, [self.field.name]),
+        yield {'selected': self.lookup_val is None and not self.lookup_null,
+               'query_string': cl.get_query_string({}, [self.field.name, self.field.name + '__isnull']),
                'display': _('All')}
         for val in self.lookup_choices:
-            val = smart_unicode(val[self.field.name])
-            yield {'selected': self.lookup_val == val,
-                   'query_string': cl.get_query_string({self.field.name: val}),
-                   'display': val}
+            if val[self.field.name] is None:
+                yield {'selected': self.lookup_null,
+                       'query_string': cl.get_query_string({self.field.name + '__isnull': 'True'}, [self.field.name]),
+                       'display': 'Is null'}
+            else:
+                val = smart_unicode(val[self.field.name])
+                yield {'selected': self.lookup_val == val,
+                       'query_string': cl.get_query_string({self.field.name: val}, [self.field.name + '__isnull']),
+                       'display': val}
 FilterSpec.register(lambda f: True, AllValuesFilterSpec)
