fromdjango.contrib.admin.filterspecsimportFilterSpecfromdjango.dbimportmodelsfromdjango.utils.safestringimportmark_safefromdjango.utils.translationimportugettextas_# FilterSpec.register places the new FilterSpec at the back# of the list. This can be a problem, because the first# matching FilterSpec is the one used.def_register_front(cls,test,factory):cls.filter_specs.insert(0,(test,factory))FilterSpec.register_front=classmethod(_register_front)classRelatedNullFilterSpec(FilterSpec):def__init__(self,f,request,params,model,model_admin):super(RelatedNullFilterSpec,self).__init__(f,request,params,model,model_admin)ifisinstance(f,models.ManyToManyField):self.lookup_title=f.rel.to._meta.verbose_nameelse:self.lookup_title=f.verbose_nameself.null_lookup_kwarg='%s__isnull'%f.nameself.null_lookup_val=request.GET.get(self.null_lookup_kwarg,None)rel_name=f.rel.get_related_field().nameself.lookup_kwarg='%s__%s__exact'%(f.name,rel_name)self.lookup_val=request.GET.get(self.lookup_kwarg,None)self.lookup_choices=f.get_choices(include_blank=False)deftitle(self):returnself.lookup_titledefchoices(self,cl):yield{'selected':self.lookup_valisNoneandself.null_lookup_valisNone,'query_string':cl.get_query_string({},[self.lookup_kwarg,self.null_lookup_kwarg]),'display':_('All')}yield{'selected':self.lookup_valisNoneandself.null_lookup_val=="True",'query_string':cl.get_query_string({self.null_lookup_kwarg:True},[self.lookup_kwarg]),'display':_('Null')}yield{'selected':self.lookup_valisNoneandself.null_lookup_val=="False",'query_string':cl.get_query_string({self.null_lookup_kwarg:False},[self.lookup_kwarg]),'display':_('Not Null')}forpk_val,valinself.lookup_choices:yield{'selected':self.lookup_val==smart_unicode(pk_val),'query_string':cl.get_query_string({self.lookup_kwarg:pk_val},[self.null_lookup_kwarg]),'display':val}FilterSpec.register_front(lambdaf:bool(f.rel),RelatedNullFilterSpec)