Ev.
Ev.

Reputation: 1567

Dynamically combine Q() - OR objects

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()

Upvotes: 3

Views: 1324

Answers (1)

justcompile
justcompile

Reputation: 3542

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})

Upvotes: 3

Related Questions