TypeError "Cannot filter a query once a slice has been taken." while running self.client.get(reverse()) function in django

I’m currently learning some basics of django using tutorials in documentation (Writing your first Django app, part 5 | Django documentation | Django) and have a problem with chapter about testing.

class QuestionDetailViewTests(TestCase):
    def test_past_question(self):
        question = create_question('text', -30)
        response = self.client.get(reverse('polls:detail', args=(question.id,)))
        self.assertContains(response, question.question_text)

create_question is just a shortcut for creating Question(models.Model) object with second argument meaning offset in days for publication date, which is set relatively to current date. ‘polls:detail’ is the name of path to this concrete view, which takes number of question as argument.

Running of py manage.py test app results in following error:

Traceback (most recent call last):
  File "C:\web\django\project1\mysite\polls\tests.py", line 59, in test_past_question
    response = self.client.get(reverse('polls:detail', args=(question.id,)))
  File "C:\web\django\project1\lib\site-packages\django\test\client.py", line 927, in get
    response = super().get(path, data=data, secure=secure, headers=headers, **extra)
  File "C:\web\django\project1\lib\site-packages\django\test\client.py", line 457, in get
    return self.generic(
  File "C:\web\django\project1\lib\site-packages\django\test\client.py", line 609, in generic
    return self.request(**r)
  File "C:\web\django\project1\lib\site-packages\django\test\client.py", line 891, in request
    self.check_exception(response)
  File "C:\web\django\project1\lib\site-packages\django\test\client.py", line 738, in check_exception
    raise exc_value
  File "C:\web\django\project1\lib\site-packages\django\core\handlers\exception.py", line 55, in inner
    response = get_response(request)
  File "C:\web\django\project1\lib\site-packages\django\core\handlers\base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\web\django\project1\lib\site-packages\django\views\generic\base.py", line 104, in view
    return self.dispatch(request, *args, **kwargs)
  File "C:\web\django\project1\lib\site-packages\django\views\generic\base.py", line 143, in dispatch
    return handler(request, *args, **kwargs)
  File "C:\web\django\project1\lib\site-packages\django\views\generic\detail.py", line 108, in get
    self.object = self.get_object()
  File "C:\web\django\project1\lib\site-packages\django\views\generic\detail.py", line 37, in get_object
    queryset = queryset.filter(pk=pk)
  File "C:\web\django\project1\lib\site-packages\django\db\models\query.py", line 1436, in filter
    return self._filter_or_exclude(False, args, kwargs)
  File "C:\web\django\project1\lib\site-packages\django\db\models\query.py", line 1448, in _filter_or_exclude
    raise TypeError("Cannot filter a query once a slice has been taken.")
TypeError: Cannot filter a query once a slice has been taken.

My code is slightly different from the one posted in tutorial, but it doesn’t work as well. All previous test of views in tutorial work properly, but they do not contain reverse() function

Please post the code for create_question.

def create_question(text, days):
    return Question.objects.create(question_text = text, pub_date = (timezone.now() + datetime.timedelta(days=days)))

There’s nothing wrong that I can see with this test code itself. That implies to me that the error is somewhere else and it’s an issue of finding that other error.

Is this the only test you have for DetailView? Or do you have the other tests as described in the tutorial? If you have other tests in that class, it may be helpful if you posted the entire class.

Please post your current polls.urls file and your detail view that you are trying to test.

It might also be informative if you divided the response = ... statement into the two statements as shown in the tutorial to see which of the two lines is causing the error to be thrown.
e.g:

url = reverse(...)
response = self.client.get(url)

In DetailView test_past_question is the only test. I’ve just tried running test with all other classes commented, except this with reverse() function - the same error is raised.

Here is polls.urls:

from django.urls import path
from . import views

app_name = 'polls'
urlpatterns = [
    path('', views.IndexView.as_view(), name = 'index'),
    path('<int:pk>/', views.DetailView.as_view(), name='detail'),
    path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
    path('<int:question_id>/vote/', views.vote, name='vote'),
]

If url is obtained on a separate line, error is thrown by response = self.client.get(url) line

Ok, so the issue is not with the reverse function. If the error is being thrown on the self.client.get call, then it appears like something is stepping on self.client. Please post the complete contents of that file.

Sorry for asking, but do you mean client.py?
Ps. i’m using django 4.2.4

No worries - I could have been more clear.

I’m referring to the file containing your QuestionDetailViewTests class. It may be helpful if we could see the complete file.

Hi. Could you also post the code of your QuestionDetailView ? I suspect the queryset attribute or get_queryset method to do the slicing, hence the error when get_object tries to filter this queryset afterward to retrieve the question from id.

Here’s the tests file. However, as mentioned previously commenting code except DetailViewTests doesn’t change anything

import datetime

from django.test import TestCase
from django.utils import timezone
from django.urls import reverse

from .models import Question

def create_question(text, days):
    return Question.objects.create(question_text = text, pub_date = (timezone.now() + datetime.timedelta(days=days)))

class QuestionModelTests(TestCase):
    def test_was_published_recently_future_question(self):
        future_question = Question(pub_date = timezone.now() + datetime.timedelta(days=30))
        self.assertIs(future_question.was_published_recently(), False)
    
    def test_was_published_recently_recent_question(self):
        time = timezone.now() - datetime.timedelta(hours=23, minutes=59, seconds=59)
        recent_question = Question(pub_date = time)
        self.assertIs(recent_question.was_published_recently(), True)
    
    def test_was_published_recently_old_question(self):
        time = timezone.now() - datetime.timedelta(days=1, seconds=1)
        recent_question = Question(pub_date = time)
        self.assertIs(recent_question.was_published_recently(), False)

class QuestionIndexViewTests(TestCase):
    def test_no_questions(self):
        response = self.client.get(reverse('polls:index'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'No polls are available')
        self.assertQuerysetEqual(response.context['latest_question_list'], [])

    def test_past_question(self):
        question = create_question('text', -30)
        response = self.client.get(reverse('polls:index'))
        self.assertQuerysetEqual(response.context['latest_question_list'], [question])

    def test_future_question(self):
        create_question('text', 30)
        response = self.client.get(reverse('polls:index'))
        self.assertQuerysetEqual(response.context['latest_question_list'], [])

    def test_past_future_question(self):
        question = create_question('text', -30)
        create_question('text', 30)
        response = self.client.get(reverse('polls:index'))
        self.assertQuerysetEqual(response.context['latest_question_list'], [question])

    def test_two_past_question(self):
        question1 = create_question('text1', -30)
        question2 = create_question('text2', -30)
        response = self.client.get(reverse('polls:index'))
        self.assertQuerysetEqual(response.context['latest_question_list'], [question1, question2])

class QuestionDetailViewTests(TestCase):
    def test_past_question(self):
        question = create_question('text', -30)
        response = self.client.get(reverse('polls:detail', args=(question.id,)))
        self.assertContains(response, question.question_text)
    
    # def test_future_question(self):
    #     question = create_question('text', 30)
    #     response = self.client.get(reverse('polls:detail', args=(question.id, )))
    #     self.assertEqual(response.status_code, 404)

Of course, here it is

class DetailView(generic.DetailView):
    model = Question
    template_name = "polls/detail.html"

    def get_queryset(self):
        return Question.objects.filter(pub_date__lte = timezone.now()).order_by("-pub_date")[:5]

Here it is.

Your queryset is sliced (by using [:5]), hence the reported error.

This view is a detail view, not a list view, so why are you ordering and limiting the number of returned records? In a detail view, there will be one single object retrieved from database (the get_object method do that by filtering the queryset on the id of the question).

Thank you, Antoine, the idea is as brilliant as dumb my error, now all works fine. Probably i shouldn’t have copied IndexView to create DetailView:) And thank you, Ken, for your persistence in searching for this bug:)