pythondjangodjango-querysetdjango-q

Django dynamically create not equal q objects


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?


Solution

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