Anyone know why this query_set doesn't return any values for me? Using filter separately, it works perfectly, so it seems .filter().filter() together is the wrong approach to filter for 'either or'.
ticket_query = request.event.tickets.filter(status='on-sale').filter(status='paused').prefetch_related('ticket_tax')
filter() with multiple parameters joins them with AND statements:
https://docs.djangoproject.com/en/2.0/ref/models/querysets/#filter
To perform OR queries in django you can use Q objects:
from django.db.models import Q
ticket_query = request.event.tickets.filter(Q(status='on-sale') | Q(status='paused')).prefetch_related('ticket_tax')
More details here:
https://docs.djangoproject.com/en/2.0/topics/db/queries/#complex-lookups-with-q
request.event.tickets.filter(status='on-sale') returns all objects with status='on-sale', and you are looking for objects with status='paused' in that list, which is why you are getting an empty queryset.
Chaining 2 filters is fine if it is a ManyToManyField where you can have multiple objects for the same field OR if you are chaining for 2 separate fields eg. request.event.tickets.filter(status='on-sale').filter(is_available=True), which returns all tickets that are on sale and are available.
The simplest approach for this problem would be to use __in in the filter like this: ticket_query = request.event.tickets.filter(status__in=['on-sale', 'paused']).prefetch_related('ticket_tax').
Hope this helps. :)
Chaining filter like you have done applies the subsequent filter to the queryset returned by the previous filter, i.e. it acts like an AND condition, not OR.
You can use the | operator to combine two queries:
ticket_query = request.event.tickets.filter(status='on-sale') | request.event.tickets.filter(status='paused')
Related
When combining QuerySets, what's the difference between the QuerySet.union() method and using the OR operator between QuerySets |?
Consider the following 2 QuerySets:
qs1 = Candidate.objects.filter(id=1)
qs2 = Candidate.objects.filter(id=2)
How is qs1 | qs2 different from qs1.union(qs2)? Is there some subtlety under the hood that I'm missing?
From the QuerySet API reference
union(), intersection(), and difference() return model instances of the type of the first QuerySet even if the arguments are QuerySets of other models.
The .union() Method return QuerySet with schema/column Names of only the first QuerySet Parameter Passed. Where as this is not the case with OR(|) Operator.
From the QuerySet API reference:
The UNION operator selects only distinct values by default. To allow duplicate values, use the all=True argument.
The .union() method allows some granularity in specifying whether to keep or eliminate duplicate records returned. This choice is not available with the OR operator.
Also, QuerySets created by a .union() call cannot have .distinct() called on them.
This is more a SQL question than a Django question. In the example you post, the Django ORM will translate qs1 | qs2 as something along the lines of
SELECT * FROM candidate WHERE id=1 OR id=2
whereas in qs1.union(qs2) it will be something like
SELECT * FROM candidate WHERE id=1 UNION SELECT * FROM candidate WHERE id=2
In this particular example there will be no difference, however I don't believe anyone would write it with UNION.
If you have an expensive query, there will be a difference in the timing when you choose one format over the other. You can use EXPLAIN to experiment. In some tests I make UNION takes way longer to give you the first row, but finishes a bit faster.
If query optimization is not an issue, it is more common to use OR.
The UNION operator is used to combine the result-set of two or more querysets. The querysets can be from the same or from different models. When they querysets are from different models, the fields and their datatypes should match.
As above definition says qs1.union(qs2) it combine both query
but OR operator is used to find boolean value it don't combine query it will just check if they are true or false and for OR operator data/query should be from one model
I want to make a query like this:
Mymodel.objects.filter(description__icontains='something' or title__icontains='something')
However using the or statement does not work. Putting a comma makes it an and statement. How do I solve this?
You wrap the conditions in Q objects [Django-doc], and use the bitwise or operator (|) as an OR:
from django.db.models import Q
Mymodel.objects.filter(
Q(description__icontains='something') |
Q(title__icontains='something')
)
I have a qs filter code like bellow:
qs2 = qs1.filter(ipv4s__ip=ip).prefetch_related(
Prefetch('ipv4s', queryset=IPv4Manage.objects.filter(ip=ip)))
now I want to add a OR logic filter in this filter.
how to add a or condition filter in this query filter?
I mean I want to add a or condition filter like this:
it will meet this
filter(ipv4s__ip=ip).prefetch_related(
Prefetch('ipv4s', queryset=IPv4Manage.objects.filter(ip=ip)))
condition or it will meet ip='1.1.1.1'.
How to make this come true?
Use an IN query; you basically are selecting on WHERE ip IN (<ip>, '1.1.1.1') here:
filter(ipv4s__ip__in=(ip, '1.1.1.1')).prefetch_related(
Prefetch('ipv4s', queryset=IPv4Manage.objects.filter(ip__in=(ip, '1.1.1.1'))))
I'm assuming that you want to filter both the questions and the related IPv4Manage objects you are prefetching here, so both ipv4s__ip__in and ip__in filters are used in the above example. If you only wanted to filter, say, the prefetch query set, adjust accordingly.
The queryset parameter otherwise takes a standard QuerySet instance, so you can use standard query constructing syntax with Q() objects to build more complex filters.
For example, if your query could not easily be satisfied with a IN (...) filter, then building on OR query works by using Q() filters combined with the | binary OR operator:
filter(Q(ipv4s__ip=ip) | Q(ipv4s__ip='1.1.1.1')).prefetch_related(
Prefetch('ipv4s', queryset=IPv4Manage.objects.filter(
Q(ip=ip) | Q(ip='1.1.1.1')
)))
Last but not least, if you are filtering your questions on the ip column of the ipv4s relationship, then you don't need to further filter the pre-fetch on the same conditions! That'll already be limited to the same filter, so it should be enough to just use:
filter(ipv4s__ip__in=(ip, '1.1.1.1')).prefetch_related('ipv4s')
The .prefetch_related('ipv4s') will prefetch the IPv4Manage objects that are connected to the questions that the filter() returned, so they have already been limited to the same ip column values.
I've been searching for a way to take the union of querysets in django. From what I read you can use query1 | query2 to take the union... This doesn't seem to work when using values() though. I'd skip using values until after taking the union but I need to use annotate to take the sum of a field and filter on it and since there's no way to do "group by" I have to use values(). The other suggestions I read were to use Q objects but I can't think of a way that would work.
Do I pretty much need to just use straight SQL or is there a django way of doing this?
What I want is:
q1 = mymodel.objects.filter(date__lt = '2010-06-11').values('field1','field2').annotate(volsum=Sum('volume')).exclude(volsum=0)
q2 = mymodel.objects.values('field1','field2').annotate(volsum=Sum('volume')).exclude(volsum=0)
query = q1|q2
But this doesn't work and as far as I know I need the "values" part because there's no other way for Sum to know how to act since it's a 15 column table.
QuerySet.values() does not return a QuerySet, but rather a ValuesQuerySet, which does not support this operation. Convert them to lists then add them.
query = list(q1) + list(q2)
In my case, I have a number of column names coming from a form. I want to filter to make sure they're all true. Here's how I currently do it:
for op in self.cleaned_data['options']:
cars = cars.filter((op, True))
Now it works but there are are a possible ~40 columns to be tested and it therefore doesn't appear very efficient to keep querying.
Is there a way I can condense this into one filter query?
Build the query as a dictionary and use the ** operator to unpack the options as keyword arguments to the filter method.
op_kwargs = {}
for op in self.cleaned_data['options']:
op_kwargs[op] = True
cars = CarModel.objects.filter(**op_kwargs)
This is covered in the django documentation and has been covered on SO as well.
Django's query sets are lazy, so what you're currently doing is actually pretty efficient. The database won't be hit until you try to access one of the fields in the QuerySet... assuming, that is, that you didn't edit out some code, and it is effectively like this:
cars = CarModel.objects.all()
for op in self.cleaned_data['options']:
cars = cars.filter((op, True))
More information here.