1

I have a requirement, that is from the client side I will get an array of objects which will have field names, filtering conditions and the filtering values like this.

Sample array of objects:

[
 {field_name: "book_name", filter_condition: "contains", filter_value: "some book name"},
 {field_name: "book_category", filter_condition: "not_equal", filter_value: "category value"},
 {field_name: "book_author", filter_condition: "starts_with", filter_value: "authoer name"},
 {field_name: "book_price", filter_condition: "equal", filter_value: 100}
]

and I've to filter based on all of the above conditions. Consider I have a model with name Book and the fields which are there in the array of objects (i.e. book_name, book_category, book_author, book_price). For filter_condition I've written a function that compares the filter_condition and assigns the Django queryset API. For example.

def assign_filter_condition(self, request, filter_condition, field_name, filter_value):

   if filter_condition == "contains":
        kwargs = {
                '{0}__icontains'.format(field_name): filter_value
            }
        return kwargs
       ...

Here I don't know how to apply conditions for exclude and NOT query.

I'm really not understanding how to write the logic for this. can anyone please help with the logic.

Thank you in advance.

Aashay Amballi
  • 1,321
  • 3
  • 17
  • 39
  • I don't know your usage case or the conditions you are working under, but you should at least have a reason to reject using Django-filter https://django-filter.readthedocs.io/en/master/index.html – nigel222 Dec 04 '19 at 09:25
  • @nigel222 The above sample array of objects I've mentioned in the question, just like that I will send a request to DRF. since you can see the filter condition and field name will change. So I don't know how to tackle it or write the logic in the backend side. – Aashay Amballi Dec 04 '19 at 09:32
  • OK, Django-filter probably isn't the right tool for your usage (DRF). Hopefully the comment will inform people who arrive here searching "dynamic filter" or suchlike. – nigel222 Dec 04 '19 at 09:37

3 Answers3

2

You can do so using dict unpacking Please note you do not need conditions so be a dict, array is enought Following code will lead you where you want.

conditions = [
 ("book_name", "contains",  "some book name"),
 ("book_category", "not_equal",  "category value"),
 ("book_author", "starts_with",  "authoer name"),
 ("book_price", "equal",  100)
]
def get_filter(values):
    name,condition,value = values
    key = f"{name}__{condition}"
    return key, value

filters = dict(map(get_filter,conditions))
qs = qs.filter(**filters)
Julien Kieffer
  • 1,116
  • 6
  • 16
2

After going through a few StackOverflow posts I figured out the logic. So below is the logic.

def get_filter(self, field_name, filter_condition, filter_value):
        # thanks to the below post
        # https://stackoverflow.com/questions/310732/in-django-how-does-one-filter-a-queryset-with-dynamic-field-lookups
        # the idea to this below logic is very similar to that in the above mentioned post
        if filter_condition.strip() == "contains":
            kwargs = {
                '{0}__icontains'.format(field_name): filter_value
            }
            return Q(**kwargs)

        if filter_condition.strip() == "not_equal":
            kwargs = {
                '{0}__iexact'.format(field_name): filter_value
            }
            return ~Q(**kwargs)

        if filter_condition.strip() == "starts_with":
            kwargs = {
                '{0}__istartswith'.format(field_name): filter_value
            }
            return Q(**kwargs)
        if filter_condition.strip() == "equal":
            kwargs = {
                '{0}__iexact'.format(field_name): filter_value
            }
            return Q(**kwargs)

        if filter_condition.strip() == "not_equal":
            kwargs = {
                '{0}__iexact'.format(field_name): filter_value
            }
            return ~Q(**kwargs)

def get(self, request):
    # getting the array of objects data to filter. The array of objects data 
    # example is in the question
    filter_data = request.query_params.getlist('filterData[]')

    all_books = Books.objects.all()
    # Creating initial Q object
    filter_objects = Q()

    # Looping through the array of objects
    for data in filter_data:
        # The main part. Calling get_filter and passing the filter object data.
        filter_objects &= self.get_filter(
                data["fieldName"], data["filterCondition"],
                data["filterValue"])
    filtered_data = all_books.filter(filter_objects)

Hope it helps someone. :)

Aashay Amballi
  • 1,321
  • 3
  • 17
  • 39
0

create a class that inherits from DjangoFilterBackends and use instead of this

class MyFilterBackend(DjangoFilterBackend):
    def get_filterset_kwargs(self, request, queryset, view):
        data = request.query_params
        data._mutable = True
        for item in data:
            try:
                print(item)
                field = getattr(queryset.model, item)
                field: DeferredAttribute
                data[item] = [x for x in list(field.field.choices) if x[1] == data[item]][0][0]
            except:
                pass
        return {
            'data': request.query_params,
            'queryset': queryset,
            'request': request,
        }