django – filter after slice / filter on queryset where results have been limited

Question:

having trouble understanding why I can’t filter after a slice on a queryset and what is happening.

stuff = stuff.objects.all()
stuff.count()

= 7

If I then go

extra_stuff = stuff.filter(stuff_flag=id)
extra_stuff.count()

= 6. Everything is all good and I have my new queryset in extrastuff no issues

stuff = stuff.objects.all()[:3]
extra_stuff = stuff.filter(stuff_flag=id)

I get the error “Cannot filter a query once a slice has been taken.”

How can I filter further on a queryset where I have limited the number of results?

Asked By: purchas

||

Answers:

You can’t use filter() after you have sliced the queryset. The error is pretty explicit.

Cannot filter a query once a slice has been taken.

You could do the filter in Python

stuff = stuff.objects.all()[:3]
extra_stuff = [s for s in stuff if s.stuff_flag=='flag']

To get the number or items in extra_stuff, just use len()

extra_stuff_count = len(extra_stuff)

Doing the filtering in Python will work fine when the size of stuff is very small, as in this case. If you had a much larger slice, you could use a subquery, however this might have performance issues as well, you would have to test.

extra_stuff = Stuff.objects.filter(id__in=stuff, stuff_flag='flag')
Answered By: Alasdair

Django gives you that error because it’s already retrieved the items from the database by that point. The filter method is only useful to refine the database query before actually executing it.

Since you’re only getting three objects, you could just do the extra filtering in Django:

extra_stuff = [s for s in stuff if s.stuff_flag==id]

but I wonder why you don’t do the filter before slicing.

Answered By: Daniel Roseman

Just do 2 queries.

total_stuff = StuffClass.objects.count()
extra_stuff = StuffClass.filter(stuff_flag=id)[:3]
extra_stuff_count = len(StuffClass.filter(stuff_flag=id))

Note, if extra_stuff_count is a few count, like 3 or 300.
Because, it's need more memory for more count (in this case, just make one more request).

Answered By: mrvol

Just made the filtering first after that create another variable and slice it like that:

 extra_stuff = stuff.objects.filter(stuff_flag=id)

 the_sliced_stuff = extra_stuff[:3] 

It works well

Answered By: AHMED dz
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.