Search code examples
pythondjangodjango-admindjango-admin-filters

Extending Django-admin's DateFieldListFilter for custom "Upcoming" filter


I am trying to add a custom "Upcoming" filter to the Django Admin DateFieldListFilter. It's really simple, just selecting dates after today. Building on this thread i was able to extend Django's standard DateFieldListFilter into my own custom one as follows :

class MyDateTimeFilter(DateFieldListFilter):

    def __init__(self, *args, **kwargs):
        super(MyDateTimeFilter, self).__init__(*args, **kwargs)

        today = datetime.now()    
        self.links += (('Upcoming'), {self.lookup_kwarg_since: today.strftime('%d %B %Y')}),

It correctly displays "Upcoming" at the bottom of my filters, but clicking does not actualy filter the results. I do not know what's wrong with my syntax and I have tried many alternatives...

Your help is very much appreciated!

PS: I am using Python 3.5.2 and Django 2.0.6


Solution

  • Without jumping straight into this one, I have done exactly what you needed for a previous project:

    In filters.py:

    import datetime
    
    from django.contrib.admin.filters import DateFieldListFilter
    from django.utils.translation import gettext_lazy as _
    
    class MyDateTimeFilter(DateFieldListFilter):
        def __init__(self, *args, **kwargs):
            super(MyDateTimeFilter, self).__init__(*args, **kwargs)
    
            now = timezone.now()
            # When time zone support is enabled, convert "now" to the user's time
            # zone so Django's definition of "Today" matches what the user expects.
            if timezone.is_aware(now):
                now = timezone.localtime(now)
    
            today = now.date()
    
            self.links += ((
                (_('Upcoming'), {
                    self.lookup_kwarg_since: str(today),
                    self.lookup_kwarg_until: str(today + datetime.timedelta(days=7)),
                }),
            ))
    

    You want to add a few declarations to be used later (e.g., today = now.date()) and then you want to use the self.lookup_kwarg_until keyword argument, and have some time range as your lookup until date (for ease, I have used today + datetime.timedelta(days=7), but you could quite easily configure this to whatever timespan you need.

    Then you'd need something like this in admin.py:

    from django.contrib import admin
    
    from filters import MyDateTimeFilter
    
    class PostAdmin(admin.ModelAdmin):
        list_filter = (
            ('published_at', MyDateTimeFilter()),
        )
    

    Disclaimer: this worked on Python 3.7, Django 2.0*. Fingers crossed this works for you!