Search code examples
pythondjangodjango-rest-frameworkdjango-filter

Django - Filter a date within a range with validation


I have a DateTimeField named 'session_start' in a model. I am trying to filter the session_start field within a date range.

Model.py

class TimeCard(models.Model):
    profile = models.ForeignKey(settings.AUTH_USER_MODEL)
    session_start = models.DateTimeField()

Filter need to follow the following conditions:

  1. Filter the "session_start" field within the range of "start_date" and "end_date".
  2. Validate "start_date" and "end_date" fields. Which means "start_date" value should be the date before the "end_date" value. if invalid data is passed, return queryset based on default values. Default values are -

start_date = 30 days back from today.

end_date = today.

I wrote the following code for filtering the "session_start" field within the range between "start_date" and "end_date". Now I am stuck at checking condition where start_date is greater than end_date. What can I do to make the validation?

filter.py

class TimeCardFilter(filters.FilterSet):

    start_date = django_filters.DateTimeFilter(name="session_start", lookup_type='gte')
    end_date = django_filters.DateTimeFilter(name="session_start", lookup_type='lte')

    class Meta:
        model = TimeCard
        fields = ['profile', 'start_date', 'end_date']

Solution

  • Based on the source code, you may have limited options. It looks like your best bet is to override the qs property.

    class TimeCardFilter(filters.FilterSet):
    
        start_date = django_filters.DateTimeFilter(name="session_start", lookup_type='gte')
        end_date = django_filters.DateTimeFilter(name="session_start", lookup_type='lte')
    
        @property
        def qs(self):
            # Parent class defines a property _qs which represents the updated queryset
            if hasattr(self, '_qs'):
               return self._qs
            # qs property is the place that FilterSet generates a query string.
            # it finds the values in the self.form.cleaned_data or self.form[name].value()
            orig_start = self.form.cleaned_data['session_start']
            orig_end = self.form.cleaned_data['session_end']
    
            # Your conditional logic would go here.
            if orig_start > orig_end:
               # You'll need real values here, these clearly are fake.
               self.form.cleaned_data['session_start'] = now()-30 * 24 * 3600
               self.form.cleaned_data['session_end'] = now()
    
            # Be sure to return FilterSet's actual QS.
            return super(TimeCardFilter, self).qs()
    
        class Meta:
            model = TimeCard
            fields = ['profile', 'start_date', 'end_date']