1

I don't know if this is obtainable but how do I combine Python's operator.and_ and operator.or_ in this Django query?

I want to get articles that have @trending or #trending and not in loaded_list from Articles model. Let's assume loaded_list = [3,4,2,5,7,8,3]

One way I did this was:

from django.db.models import Q
articles = Articles.objects.filter(Q(content__icontains = '@trending') | Q(content__icontains = '#trending') & ~Q(id__in = loaded_list))[:5]

The code below is just to show what I want to achieve but how do I write it correctly?

import operator
from django.db.models import Q

query = reduce(operator.and_|or_,[(['@trending' or '#trending']), ~Q(id__in = loaded_list )])
articles = Articles.objects.filter(query)
4
  • you can try using exclude: docs.djangoproject.com/en/1.7/topics/db/queries/… Commented Feb 28, 2015 at 21:39
  • @PeterDeGlopper: It will work, of course but I want to be able to do it with operator.and_ and operator.or_ combined in a single query. Commented Feb 28, 2015 at 21:44
  • Can you clarify what you want that your example with | a d & does not accomplish? I generally would not use reduce when the desired filters are known when you're writing the code. Commented Feb 28, 2015 at 21:45
  • The first example will do it but I want to know if what I am asking for can be done and how it can be done. Commented Feb 28, 2015 at 21:48

1 Answer 1

8

I'm still not sure why you'd want to do this the way you're describing, but it is certainly possible to write the expression you gave with operator.or_ and operator.and_. Doing so will give you no benefits over using | and & to express the same logic, but it's legal.

operator.or_ and operator.and_ are functional wrappers for the same operation the | and & operators perform. Generally you use them in cases where having the operation available as a callable is useful, such as calling reduce over a sequence that's not known at the time you're writing the code. For example, this is a concise (but utterly un-optimized, please don't actually use it) factorial function for positive integers:

import operator
def slow_factorial(n):
    return reduce(operator.mul, xrange(1, n+1))

One clause of your query is:

Q(content__icontains = '@trending') | Q(content__icontains = '#trending')

That can also be written as:

operator.or_(Q(content__icontains = '@trending'),
             Q(content__icontains = '#trending'))

You can achieve the same ending Q object (probably less efficiently, but I haven't actually profiled) with:

some_q = reduce(operator.or_,
                (Q(content__icontains = '@trending'),
                 Q(content__icontains = '#trending')))

That Q object can then be combined with another:

articles = Articles.objects.filter(some_q & ~Q(id__in = loaded_list))

Which is identical to:

articles = Articles.objects.filter(operator.and_(some_q, ~Q(id__in = loaded_list)))

Or, again as a less readable expression:

query = reduce(operator.and_,
               (some_q,
                ~Q(id__in=loaded_list)))
articles = Articles.objects.filter(query)

Or being totally explicit:

query = reduce(operator.and_,
               (reduce(operator.or_,
                       (Q(content__icontains = '@trending'),
                        Q(content__icontains = '#trending'))),
                ~Q(id__in=loaded_list))

I think I've gotten the parens right there, but I wouldn't be shocked to learn that I've balanced them wrong - the ease of making that kind of mistake is just one reason this is an inferior implementation compared to the simple expression you used in your question.

I hope to be able to amend this answer if you can explain more about what you're trying to do and why it's desirable to use reduce and the operator names rather than the built in operator syntax. Is the list of terms you want to search for unknown at coding time and also of unknown length? That's the first case I can think of where reduce etc would help.

Sign up to request clarification or add additional context in comments.

1 Comment

Can't thank you enough for this thorough explanation.

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.