0

I'm working on creating dynamic filters using nested AND / OR logic, and would like to include NOT as an option. This leverages Q objects to create the nested AND / OR logic.

The class takes in a json object like so:

filters = {
    'and': {
        "url__is": "www.test.com",
        "name__is": "test"
    }
}

This gets compiled down to

.filter(Q(url__is='www.test.com') and Q(name__is='test')

This works via a recursive function that really just does this on each level of the json tree.

return source_queryset.filter(reduce(and_, filter_list))

and_ is from the python operator library and has been working great. I'd like to add NOT as an option as well though, and can't seem to find an equivalent option to reduce a list of Q objects to.

Does anyone know a way to use reduce in a way that creates the idea of not equal to a list of q objects?

2 Answers 2

1

You're looking for the ~ operator on a Q object, which is a complex lookup.

In the end you would have something like this:

.filter(~Q(url__is='www.test.com'))

To match your recursive function you should use the inv or invert operator. Because it matches the desired output when using ~ operator.

>>> from django.db.models import Q
>>> from operator import inv
>>> ~Q()
<Q: (NOT (AND: ))>
>>> inv(Q())
<Q: (NOT (AND: ))>

So you can use it in your recursive function with something like this:

return source_queryset.filter(map(inv, filter_list))
Sign up to request clarification or add additional context in comments.

6 Comments

Using reduce() with inv() throws the following error: inv() takes exactly one argument (2 given). Any thought on a work around for that? @bernardo
@Khakis7 What are the parameters that you're using? (What is the value of filter_list?)
@Khakis7 Sorry for that. Since ~ is an unary operator, you have to use map instead of reduce.
@Khakis7 If you intend to use and after ~ you can simply do the following: source_queryset.filter(recude(and_, map(inv, filter_list)))
it seems to be creating a very weird result set using map '(NOT (AND: (NOT (AND: ('item_id', 'random string'))), (NOT (AND: ('url', 'google.com/')))))'
|
0

You can flip any Q object by applying the ~ operator in front of it.

.exclude(Q(id=5))

is the same as:

.filter(~Q(id=5))

You should be able to use this when calculating filter_list.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.