Reputation: 258
I have multiple conditional queries based on whether a variable is set or not and the value of 'doc_type'. But I think it looks ugly and I'm repeating code. Is there a clearer way to get this done?
if status is not None:
if doc_type == 4:
invoices = Invoice.objects.filter(Q(type=4) | Q(type=5) | Q(status=status))
else:
invoices = Invoice.objects.filter(type=doc_type, status=status)
else:
if doc_type == 4:
invoices = Invoice.objects.filter(Q(type=4) | Q(type=5))
else:
invoices = Invoice.objects.filter(type=doc_type)
Upvotes: 0
Views: 267
Reputation: 23871
If one status
is OR
and another status
is AND
, remember QuerySet
supports |
and &
as well:
qs = Invoice.objects.filter(doc_type__in=(doc_type, 5) if doc_type == 4 else (doc_type,))
if status is not None:
if doc_type == 4:
qs |= Invoice.objects.filter(status=status)
else:
qs = qs.filter(status=status)
Upvotes: 0
Reputation: 308799
Are you sure this is what you want?
invoices = Invoice.objects.filter(Q(type=4) | Q(type=5) | Q(status=status))
This will return all invoices where type=4
OR type=5
OR status=Status
.
I think you mean (type is 4 or 5) AND status=status? If so, you can do your Query as follows:
types = [doc_type]
if doc_type == 4:
types.append(5)
invoices = Invoice.objects.filter(type__in=types)
if status is not None:
invoices = invoices.filter(status=status)
You can use Q()
objects if you prefer, but I think that chaining filters is easier to understand in this case.
Upvotes: 4
Reputation: 368944
Q
objects can be combined using |
or &
.
Using operator.or_
(|
) and operator.and_
(&
) which works as functions instead of operator, the code can be expressed as:
import operator
op = operator.and_
q = Q(type=doc_type) # This is always included.
if doc_type == 4:
# When doc_type == 4, conditions are combine with `|`, otherwise `&`
op = operator.or_
q = Q(type=doc_type) | Q(type=5)
if status is not None:
q = op(q, status=status)
invoices = Invoice.objects.filter(q)
Upvotes: 4