Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django admin list_filter - filter field by is empty (None or empty string "")

In models.py I have:

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

And in admin.py:

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

admin.site.register(User, UserAdmin)

I just want to filter Users by have image or not (null or empty string)

But django shows filter by image urls =)

Is there a way to make list_filter = ['image'] behave like boolean field?

Big thx for advices!

like image 429
MaxCore Avatar asked Jun 13 '16 21:06

MaxCore


People also ask

How check field is null in Django?

null=True will make the field accept NULL values. Blank values for Django field types such as DateTimeField or ForeignKey will be stored as NULL in the database.

What is list filter in Django admin?

Django allows the user of the admin site to filter the instances of a Model by adding the list_filter attribute to your ModelAdmin objects. You can find more information about the Django's filtering utilities in the official documentation, in the Django Admin Site section.


4 Answers

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]
like image 79
MaxCore Avatar answered Oct 19 '22 22:10

MaxCore


As of Django 3.1, you can use EmptyFieldListFilter as follows:

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

See the docs for details. See the release notes as well.

The code is available here, if you need to customize it or backport it.

like image 9
DylanYoung Avatar answered Oct 19 '22 23:10

DylanYoung


Based on MaxCore's answer, I created customised class that I can use to modify title and parameter name:

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"
like image 6
K.H. Avatar answered Oct 19 '22 23:10

K.H.


K.H.'s approach of a base class that could be reused easily was really helpful for me. I couldn't get the written example to work, but with a small tweak it worked perfectly (Python 2.7, Django 1.10) to achieve this.

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, ...]
like image 3
Skuli Arnlaugsson Avatar answered Oct 20 '22 00:10

Skuli Arnlaugsson