5

Is it possible to modify Django Q() objects after construction? I create a Q() object like so:

q = Q(foo=1)

is it possible to later change q to be the same as if I had constructed:

q2 = Q(foo=1, bar=2)

? There's no mention of such an interface in the Django docs that I could find.

I was looking for something like:

Q.append_clause(bar=2)
Tomasz Jakub Rup
  • 10,502
  • 7
  • 48
  • 49
Scott Stafford
  • 43,764
  • 28
  • 129
  • 177

3 Answers3

6

You can just make another Q() object and AND them together: q2 = q & Q(bar=2)

Perrin Harkins
  • 221
  • 2
  • 4
  • 2
    Are you sure that's semantically identical? It may well be, but I know there can be gotchas. For instance, calling `.filter(Q(parent__foo=1)).filter(Q(parent__bar=1))` is different than `.filter(Q(parent__foo=1, parent__bar=1))` and I didn't want to fall afoul of anything like that. – Scott Stafford Sep 03 '14 at 14:24
  • 1
    Yes, they are identical. What matters is if there are two .filter calls or just one. – akaariai Sep 03 '14 at 19:04
5

You can add Q objects together, using their add method. For example:

>>> q = Q(sender=x)
>>> q.add(Q(receiver=y), Q.AND)

The second argument to add is the connector, which can also be Q.OR

EDIT: My answer is merely a different way of doing what Perrin Harkins suggested, but regarding your other concern, about different behavior of filter depending on the way you construct the query, you don't have to worry about that if you join Q objects. My example is equivalent to filter(sender=x, receiver=y), and not filter(sender=x).filter(receiver=y), because Q objects, as far as I could see in a quick test, do an immediate AND on the clauses and don't have the special behavior of filter for multi-valued relations.

In any case, nothing like looking at the SQL and making sure it really is doing the same in your specific queries.

Paulo Almeida
  • 7,803
  • 28
  • 36
0

The answers here are a little old and unsatisfactory imo. So here is my answer

This is how you deep copy:

def deep_copy(q: Q) -> Q:
    new_q = Q()
    # Go through the children of a query: if it's another
    # query it will run this function recursively
    for sub_q in q.children:
        # Make sure you copy the connector in 
        # case of complicated queries
        new_q.connector = q.connector
        if isinstance(sub_q, Q):
            # This will run recursively on sub queries
            sub_q = get_employee_q(sub_q)
        else:
            pass # Do your modification here
        new_q.children.append(sub_q)
    return new_q

In the else condition is where your stuff (name='nathan' for example) is defined. You can change or delete that if you'd like and the Query should work fine.

Ysrninja
  • 129
  • 1
  • 2
  • 9