-
Notifications
You must be signed in to change notification settings - Fork 768
Description
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?