【问题标题】:Django admin list_filter - filter field by is empty (None or empty string "")Django admin list_filter - 过滤字段为空(无或空字符串“”)
【发布时间】:2016-06-13 21:48:58
【问题描述】:

在 models.py 我有:

class User(modals.Model):
    name = models.CharField(max_length=255)
    image = models.ImageField(blank=True, null=True)

在 admin.py 中:

class UserAdmin(admin.ModelAdmin):
    list_filter = ['image']

admin.site.register(User, UserAdmin)

我只想通过是否有图像来过滤用户(null 或空字符串)

但 django 显示按图片网址过滤 =)

有没有办法让list_filter = ['image'] 表现得像布尔字段?

非常感谢您的建议!

【问题讨论】:

    标签: django django-admin


    【解决方案1】:

    从 Django 3.1 开始,您可以使用 EmptyFieldListFilter,如下所示:

    list_filter = (
        ("my_fk_field", admin.EmptyFieldListFilter),
    )
    

    详情请参阅the docs。参见the release notes

    代码在here 可用,如果您需要对其进行自定义或反向移植。

    【讨论】:

    • 仅供参考:向后移植也很容易。
    【解决方案2】:

    admin.py

    class ImageListFilter(admin.SimpleListFilter):
    
        title = _('Has photo')
    
        parameter_name = 'has_photo'
    
        def lookups(self, request, model_admin):
    
            return (
                ('yes', _('Yes')),
                ('no',  _('No')),
            )
    
        def queryset(self, request, queryset):
    
            if self.value() == 'yes':
                return queryset.filter(image__isnull=False).exclude(image='')
    
            if self.value() == 'no':
                return queryset.filter(Q(image__isnull=True) | Q(image__exact=''))
    
    
    class UserAdmin(admin.ModelAdmin):
    
        list_filter = [ImageListFilter]
    

    【讨论】:

      【解决方案3】:

      一个基于 MaxCore 的回答的更灵活的版本,它可以即时创建新类

      def by_null_filter(attr, name, null_label='is Null', non_null_label='not Null', bool_dt=False, bool_value=False):
      
          class ByNullFilter(admin.SimpleListFilter):
              """List display filter to show null/not null values"""
              parameter_name = attr
              title = name
      
              def lookups(self, request, model_admin):
                  if bool_dt:
                      label_null = 'not %s' % attr
                      label_non_null = attr
                  elif bool_value:
                      label_null = 'no'
                      label_non_null = 'yes'
                  else:
                      label_null = null_label
                      label_non_null = non_null_label
      
                  return (
                      ('not_null', label_non_null),
                      ('null', label_null)
                  )
      
              def queryset(self, request, queryset):
                  filter_string = attr + '__isnull'
                  if self.value() == 'not_null':
                      is_null_false = {
                          filter_string: False
                      }
                      return queryset.filter(**is_null_false)
      
                  if self.value() == 'null':
                      is_null_true = {
                          filter_string: True
                      }
                      return queryset.filter(**is_null_true)
      
          return ByNullFilter
      
      

      用法 如果您有 已处理 DateTime 字段并且您想对其进行过滤 - 它是否有值或 null

      # with default filter labels (`not Null`, `is Null`)
      list_filter = (by_null_filter('processed', 'Processed'), )
      
      # Processed header with labels based on field name (`processed`, `not processed`)
      list_filter = (by_null_filter('processed', 'Processed', bool_dt=True), )
      
      # Paid header filter with labels based on bool(end_time) (`yes`, `no`)
      list_filter = (by_null_filter('end_time', 'Paid', bool_value=True), )
      

      【讨论】:

        【解决方案4】:

        根据 MaxCore 的回答,我创建了可用于修改标题和参数名称的自定义类:

        from django.contrib import admin
        from django.utils.translation import ugettext_lazy as _
        from django.db.models import Q
        
        class NotNullFilter(admin.SimpleListFilter):
            title = _('Filter title not set')
        
            parameter_name = 'parameter name not set'
        
            def lookups(self, request, model_admin):
        
                return (
                    ('not_null', _('Not empty only')),
                    ('null', _('Empty only')),
                )
        
            def queryset(self, request, queryset):
        
                if self.value() == 'not_null':
                    is_null_false = {
                        self.parameter_name + "__isnull": False
                    }
                    exclude = {
                        self.parameter_name: ""
                    }
                    return queryset.filter(**is_null_false).exclude(**exclude)
        
                if self.value() == 'null':
                    is_null_true = {
                        self.parameter_name + "__isnull": True
                    }
                    param_exact = {
                        self.parameter_name + "__exact": ""
                    }
                    return queryset.filter(Q(**is_null_true) | Q(**param_exact))
        
        
        class YoutubeNotNullFilter(NotNullFilter):
            title = "Youtube"
            parameter_name = "youtube_videoid"
        

        【讨论】:

          【解决方案5】:

          K.H. 的可轻松重用的基类方法对我非常有帮助。我无法让书面示例正常工作,但稍作调整后(Python 2.7、Django 1.10)就可以完美地实现这一目标。

          from django.contrib import admin
          
          
          class NotNullFilter(admin.SimpleListFilter):
              title = 'Filter title not set'
              parameter_name = 'parameter name not set'
          
              def lookups(self, request, model_admin):
                  return (
                      ('not_null', 'Not empty only'),
                      ('null', 'Empty only'),
                  )
          
              def queryset(self, request, queryset):
                  filter_string = self.parameter_name + '__isnull'
                  if self.value() == 'not_null':
                      is_null_false = {
                          filter_string: False
                      }
                      return queryset.filter(**is_null_false)
          
                  if self.value() == 'null':
                      is_null_true = {
                          filter_string: True
                      }
                      return queryset.filter(**is_null_true)
          
          
          class YoutubeNotNullFilter(NotNullFilter):
              title = "Youtube"
              parameter_name = "youtube_videoid"
          
          
          class SomeVideoClass(admin.ModelAdmin):
              ...
              list_filter = [YouTubeNotNullFilter, ...]
          

          【讨论】:

            猜你喜欢
            • 2012-10-18
            • 2021-06-19
            • 2013-05-15
            • 2017-01-21
            • 1970-01-01
            • 2013-10-11
            • 1970-01-01
            • 2010-10-09
            • 2018-03-05
            相关资源
            最近更新 更多