TransWikia.com

How can I dynamically set Django filter in queryset

Stack Overflow Asked by Kimor on December 11, 2021

I am doing a table in React, fetching some data from my Django DB.
It has filters and I want them to call the API to get results if needed.

Thing is I have to duplicate a lot of lines whereas I am pretty sure there is a better way to do this.

Here is some part of the code:

        if ss_value and not es_value and not iv_value and not timestamp:
            queryset = DailyReport.objects.all().filter(station_departure=ss_value)
        elif not ss_value and es_value and not iv_value and not timestamp:
            queryset = DailyReport.objects.all().filter(station_arrival=es_value)
        elif not ss_value and not es_value and iv_value and not timestamp:
            queryset = DailyReport.objects.all().filter(is_virtual=iv_value)
        elif not ss_value and not es_value and not iv_value and timestamp:
            queryset = DailyReport.objects.all().filter(
                Q(timestamp__range=(min_dt, max_dt)) | Q(upload_timestamp__range=(min_dt, max_dt)))
            logger.debug(queryset)
        elif ss_value and es_value and not iv_value and not timestamp:
            queryset = DailyReport.objects.all().filter(station_departure=ss_value, station_arrival=es_value)
        elif ss_value and not es_value and iv_value and not timestamp:
            queryset = DailyReport.objects.all().filter(station_departure=ss_value, is_virtual=iv_value)

and it goes on and on.

Do you have any idea of a way to do it in a cleaner way ??

Thank you 🙂

3 Answers

You could utilize the dictionary unpacking using **. When you have a list of filters, add them to a dictionary then unpack them into the queryset filter.

For Example:

Model.objects.filter(x=2, y=3)

# equivalent to

Model.objects.filter(**{"x":2, "y":3})

So your code can be done like so:

    queryset_filters = {}    
    if ss_value:
       queryset_filters['station_departure'] = ss_value
    if es_value:
       queryset_filters['station_arrival'] = es_value
    .
    .
    .
    
    queryset = DailyReport.objects.filter(**queryset_filters)

Answered by Zakaria Talhami on December 11, 2021

The technique you're missing has less to do with django and more to do with Python in general.

def myfunc1(arg1, arg2):
    print(arg1, arg2)

def myfunc2(arg1=None, arg2=None):
    print(arg1, arg2)

mylist = ['val1', 'val2']
mydict = {'arg1': 'val1', 'arg2': 'val2'}

Assuming you have the above:

myfunc1('val1', 'val2')
myfunc1(*mylist)

Are equivalent! Similarly:

myfunc2('val1', 'val2')
myfunc2(**mydict)

Are also equivalent!

You can pass a list into a function call as if they're the positional arguments with a single *, and you can pass a dictionary as keyword arguments with a double *

So ultimately what you want to do is build up a dictionary of things of the form:

filter_kwargs = {
   'django_filter_kwarg_name': 'django_filter_value'
}

So for you this might be:

# build up the dictionary (or maybe you can do this off the form, request.GET, etc
filter_kwargs = {
    'station_departure': ss_value,
    'station_arrival': es_value,
    ....
}
# do something here to filter out the empty/None key/values
filter_kwargs = {key: value if value for key, value in filter_kwargs.items}
# now get the queryset
queryset = DailyReport.objects.all().filter(**filter_kwargs)


Answered by Mike Sandford on December 11, 2021

You can try assigning form actions to your buttons:

<form action="" method="post">
  <input type="submit" name="value1" value="Button Name" />
  <input type="submit" name="value2" value="Button Name" />
</form>

And then you can do

if "value1" in request.POST:
    qs = DailyReport.objects.filter(station_departure="value1")

Note that you will need to buttons, as there won't be any point if there's only one and HTML wouldn't allow so.

Reference

Answered by crimsonpython24 on December 11, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP