0

I am asking for a suggestion abount how to detect which filters are being used by the user, a filtering system can have different options to get the data, but using if statements to check if a value comes in a POST and then add it to a filters set is not really a good option specially when there a lot of them.

# Some if statements detecting if a filter is used (if it is not null in the POST)
# Adding the filter to filters

filters = {
# filters after being added
            '{}__{}'.format('categories', 'exact'): request.POST['category'],
            '{}__{}'.format('price', 'gte'): request.POST['price'], # Only an example
        }
        products = Product.objects.filter(**filters)

This works, but i just want to know what would you recommend.

2 Answers2

2

If I understood your question correctly, I would chain filters instead:

queryset = Product.objects.all()

if 'category' in request.POST:
    queryset.filter(categories__exact=request.POST['category'])

if 'price' in request.POST:
    queryset.filter(price__gte=request.POST['price'])
Gasanov
  • 2,839
  • 1
  • 9
  • 21
  • Yeah, but what if i have too many filters, that would be too much code, thanks for the suggestion i like it. –  May 17 '19 at 18:25
  • You can check this post https://stackoverflow.com/a/14958252/924300 for dynamic query in django – Dimitris Kougioumtzis May 17 '19 at 18:30
  • @Gasanov just one question, queryset = Product.objects.all() won't bring the whole table? (all the data) and then execute filtering –  May 17 '19 at 18:32
  • @DanielBurgos it will bring whole table in case you don't have any filter attached (if POST is empty). But don't worry, it will not spam your database with queries up until you [try to access it](https://docs.djangoproject.com/en/2.2/topics/db/queries/#querysets-are-lazy) – Gasanov May 17 '19 at 18:34
  • @DanielBurgos if you have filter-heavy application, you can check out [`django-filters`](https://django-filter.readthedocs.io/en/master/) – Gasanov May 17 '19 at 18:37
1

To expand on Gasanov's suggestion:


possible_filters = {
    'category': 'exact',
    'price': 'gte',
    # etc. Not sure if this can be done any smarter
    # maybe if you pass `cateogry__exact` in the POST data instead of just `category`?
}

queryset = Product.objects.all()
for key, val in request.POST.items():
    if key in possible_filters:
        filter_kwargs = {
            f'{key}__{possible_filters[key]}': val,
        }
        queryset = queryset.filter(**filter_kwargs)

Or you can build up the kwargs and have a single call to filter. Unless you are filtering over reverse FK relationships or M2M relationships, the two are pretty much the same (docs for when they are not the same are here)

filter_kwargs = {}
for key, val in request.POST.items():
    if key in possible_filters:
        filter_key = f'{key}__{possible_filters[key]}'
        filter_kwargs[filter_key] = val
queryset = queryset.filter(**filter_kwargs)
frnhr
  • 12,354
  • 9
  • 63
  • 90