Skip to content

Add support for SplitDateTimeField #1729

@LeoWelter

Description

@LeoWelter

maybe I'm overlooking something obvious completely, please forgive me if so, I'm still learning Django and Python. Maybe there is a very simple solution.

rationale: we often filter on dates, i.e. a date range. But sometimes we want to filter including a specific time as well:
example: all visitors from a day 2025-09-19 10:00h to 2025-09-19 13:00h

Django does have SplitDateTimeField for ages and I used it in some forms.

for example:

class VisitorForm(forms.Form):
     date_from = forms.SplitDateTimeField(required=False, 
                                         label='Date From', 
                                         widget=forms.SplitDateTimeWidget(
                                            date_attrs={'type': 'date', 'class': 'form-control'},
                                            time_attrs={'type': 'time', 'class': 'form-control','value': '09:00'},
                                            date_format='%Y-%m-%d',
                                            time_format='%H:%M'  
                                         ))

This results in two GET parameters upon submit: date_from_0 (containing the date) and date_from_1 (containing the time)
Normally this is handled by the form clean method, returning a nice combined DateTime object for further filtering.

However, it does not play nice with django-filter, when you want to use the filter.form as the form in your template (prevent duplication of code). The GET parameters of course do not match the intended field name due to the split.

I did not find a way to use this widget in a FilterSet that also acts as form in the template, (I'm using FilterView from django-tables2 btw)

tl;dr: I now create an extra form, matching the filterset and I have to do something ugly in the Filter's init to make it work

class VisitorFilter(FilterSet):
     date_from = DateTimeFilter(field_name='date',
                                             lookup_expr='gte')
     
     def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # make copy of immutable self.data
        data = self.data.copy()
        
        #get the date and time from the GET parameters in 'data' and create a datetime object
        date_str = data.get("date_to_0")
        time_str = data.get("date_to_1")
        date_obj = datetime.strptime(date_str, "%Y-%m-%d").date()
        if time_str:
            time_obj = datetime.strptime(time_str, "%H:%M").time()
        else:
            time_obj = time(9, 0)  # default to 09:00
        naive_dt = datetime.combine(date_obj, time_obj)

        # Make timezone-aware and assign to the right 'field' parameter
        aware_dt = timezone.make_aware(naive_dt, timezone.get_current_timezone())
        data['date_to'] = aware_dt

        # replace self.data with the modified copy
        self.data = data        

This works, but it feels clumsy. Any options?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions