3

I am trying to create a dynamic search in this ListView I have. My idea is to specify the fields and the type of search each time I try to inherit this view.

My problem is that every time I try to make a search, it works only on the first field of the tuple. In my example: requests__email is the first field, and when I print the object query_q after making a query for 'app', I get the following output:

(OR: (AND: ), ('requests__email__icontains', 'app'), (AND: ('requests__email__icontains', 'app'), ('status__icontains', 'app')), (AND: ('requests__email__icontains', 'app'), ('status__icontains', 'app'), ('license_type__name__icontains', 'app')))

I don't understand why, because I am using the operator I thought it would work |= in query_q |= Q(**query_kwargs). If I try to make a search based on the other attributes, status for example, the search doesn't work.

views.py

class DefaultListView(ListView):
    searchable_fields = (
        ('requests__email', 'icontains'),
        ('status', 'icontains'),
        ('license_type__name', 'icontains'),
    )

    def get_queryset(self):
        form = self.form_class(self.request.GET)

        if form.is_valid():
            if not self.searchable_fields:
                raise AttributeError('searchable_fields has not been configured.')
            term = form.cleaned_data['term']
            query_kwargs = dict()
            query_q = Q()

            # Build a list of Q objects based on the searchable attribute
            for field, search_type in self.searchable_fields:
                query_kwargs["{0}__{1}".format(field, search_type)] = term
                query_q |= Q(**query_kwargs)

            ordering = self.get_ordering()
            queryset = self.model.objects.filter(query_q)

            if ordering:
                return queryset.order_by(**ordering)

            return queryset
        return super(DefaultListView, self).get_queryset()
Community
  • 1
  • 1
Ev.
  • 1,537
  • 1
  • 24
  • 49

1 Answers1

3

If you are wanting to build the query as X = Y OR W = Z it's because of

query_kwargs["{0}__{1}".format(field, search_type)] = term

You're adding more keys to query_kwargs with each iteration of the loop rather than re-creating the variable Would suggest something like this

for field, search_type in self.searchable_fields:
    query_q |= Q(**{"{0}__{1}".format(field, search_type): term})
justcompile
  • 3,362
  • 1
  • 29
  • 37
  • Aaahhh. Very true. Totally didn't pay attention to that. Thanks for highlighting it. :) – Ev. Mar 09 '18 at 16:21