3

I want to filter a queryset by a date range where both the start and end date are optional. Specifically,

if dt_from:
    results = results.filter(date_modified__gte=dt_from)
if dt_until:
    results = results.filter(date_modified__lte=dt_until)

where dt_from and dt_until are each either datetime.datetime, datetime.date, or None. The documentation about the behaviour of chaining multiple filters is extremely confusing however (see Chaining multiple filter() in Django, is this a bug?), and I'm not sure that the above does what I think it does (it may OR the filters rather than ANDing them).

Does the above code achieve what I want (i.e. AND the two filters) or is there another way I should do this?

Community
  • 1
  • 1
Flash
  • 15,945
  • 13
  • 70
  • 98
  • If you go with this approach, don't forget to update the `results` variable: `results = results.filter(date_modified__gte=dt_from)` and `results = results.filter(date_modified__lte=dt_until)`. – alecxe Jan 09 '17 at 02:05
  • @alecxe right, thanks :) – Flash Jan 09 '17 at 02:11
  • That linked question is not at all relevant, since it refers to queries across relationships. The docs are quite clear here (and indeed are referenced in that accepted answer): "Successive filter() calls further restrict the set of objects". – Daniel Roseman Jan 09 '17 at 07:38

1 Answers1

1

I have a generic solution for this kind of problems. Reuse this custom queryset for all models

class MyQuerySet(models.QuerySet):

    def filter_if(self, **kwargs):
        new_kwargs = {a: b for (a, b) in kwargs.items() if b}
        return self.filter(new_kwargs)

results.filter_if(date_modified__gte=dt_from, date_modified__lte=dt_until)
Vinayak Kaniyarakkal
  • 1,110
  • 17
  • 23