Best practices for writing filtering utilities for models

Taking the Book model as an example, sometimes I’ll write utility functions that can be used to filter the books. For example, filter books by this author, within this published date range, etc.

Are there best practice guidelines around how to write these filter functions? Mainly, is it better to execute .filter() and return the QuerySet result, or is it better to return Q() objects and let the consumer perform the actual filter.

From what I’ve seen online, usually people use .filter() and return that, but I wanted to check here before assuming it’s the best way to do it.

Hello there!
I think it’s a matter of usage rather than “best” way to do it.
From my experience,
I tend to write functions that return:

  • A QuerySet when the caller next filter calls target exclusive AND conditions, meaning that I won’t need to chain any OR expressions on the queryset. But only when the filtering is really important to be done right, or it’s repeated across several different places. You can also write a model Manager instead of a function (I personally don’t like managers). One thing to note here, is that for testing you would need to issue a query to the database (by evaluating the queryset) in order to test if the conditions are properly met.
  • A Q object when the caller possibly would want to join this Q condition with another Q objects, such as when such other condition is met, a OR is used, or when such other condition is met a AND is used. For me, this is not that common, most of the times I build the Q object inside the function that returns the QuerySet. With this approach, for testing you don’t need to issue a query to the database to test if the condition is met.

But sometimes, if the filter is “too simple” I just call filter wherever I need, instead of creating a function for a simple filtering.

Side note: Don’t bother too much on if you should use X or Y approach when the framework lets you pick both of them. Just use the one you think it makes sense for the current use case.

1 Like