Django — Не удается отфильтровать запрос после того, как был взят фрагмент

#python #django #django-views

Вопрос:

Я использую представления на основе классов, и когда я пытаюсь использовать «paginate_by = 6» с «def get_context_data(self, **kwargs):», я получаю следующую ошибку:

 AssertionError at /

Cannot filter a query once a slice has been taken.

Request Method:     GET
Request URL:    http://127.0.0.1:8000/
Django Version:     3.2
Exception Type:     AssertionError
Exception Value:    

Cannot filter a query once a slice has been taken.

Exception Location:     C:Usersxxxxxxxxxxxxenvlibsite-packagesdjangodbmodelsquery.py, line 953, in _filter_or_exclude
Python Executable:  C:UsersxxxxxxxxxxxxenvScriptspython.exe
Python Version:     3.9.1
Python Path:    

['C:\Users\xxxx\xxxx\xxxx',
 'C:\Program Files\Python39\python39.zip',
 'C:\Program Files\Python39\DLLs',
 'C:\Program Files\Python39\lib',
 'C:\Program Files\Python39',
 'C:\Users\xxxx\xxxx\xxxx\env',
 'C:\Users\xxxx\xxxx\xxxx\env\lib\site-packages']

Server time:    Sat, 29 May 2021 00:11:20  0000
 

Я пытаюсь выполнить разбиение на страницы для результата фильтров, например, если фильтры не применены, он должен выполнить разбиение на страницы для всех задач.

Я нашел только решение, которое не использует функции с помощью пагинатора. Я хотел бы знать, возможно ли это сделать с представлением на основе классов и как, я немного потерялся в этом.

мой views.py:

 class TaskList(LoginRequiredMixin, ListView):
    model = Task
    context_object_name = "tasks"
    template_name = "todo/tasks.html"
    paginate_by = 6

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["tasks"] = context["tasks"].filter(user=self.request.user)
        context["count"] = context["tasks"].filter(complete=False).count()
        context["projects"] = Project.objects.all()

        search_input = self.request.GET.get("search") or ""
        project_input = self.request.GET.get("project") or ""
        complete_input = self.request.GET.get("complete") or ""

        if search_input:
            context["tasks"] = context["tasks"].filter(title__icontains=search_input)
            context["count"] = context["tasks"].filter(complete=False).count()

        context["search_input"] = search_input

        if project_input:
            context["project_selected"] = project_input
            context["tasks"] = context["tasks"].filter(
                project__name__icontains=project_input
            )
            context["count"] = context["tasks"].filter(complete=False).count()

        if complete_input == "all":
            context["complete_selected"] = complete_input
            context["tasks"] = context["tasks"].filter(user=self.request.user)
            context["count"] = context["tasks"].filter(complete=False).count()
        else:
            if complete_input:
                context["complete_selected"] = complete_input
                context["tasks"] = context["tasks"].filter(
                    user=self.request.user, complete=True
                )
                context["count"] = context["tasks"].filter(complete=True).count()
            else:
                context["complete_selected"] = ""
                context["tasks"] = context["tasks"].filter(
                    user=self.request.user, complete=False
                )

        return context
 

мой models.py:

 class Task(models.Model):
    project = models.ForeignKey(Project, on_delete=CASCADE)
    user = models.ForeignKey(User, on_delete=SET_NULL, null=True, blank=True)
    title = models.CharField(max_length=255)
    description = RichTextField(null=True, blank=True)
    complete = models.BooleanField(default=False)
    time = models.TimeField(default=datetime.time(00, 00))
    date = models.DateField(default=datetime.date.today)

    def __str__(self):
        return self.title

    class Meta:
        ordering = ["complete", "-date", "title"]
 

при необходимости я могу добавить дополнительную информацию

Ответ №1:

создайте глубокую копию контекста [«задачи»] и примените нарезку к копии объекта вместо основного объекта

Комментарии:

1. Не могли бы вы привести мне пример того, как это должно быть сделано?

Ответ №2:

Я сделал обходной путь, где я применяю разбиение на страницы к контексту[«задачи»], а затем делаю копию URL-адреса, чтобы поддерживать фильтрацию на каждой странице.

views.py:

 class TaskList(LoginRequiredMixin, ListView):
model = Task
context_object_name = "tasks"
template_name = "myapp/list.html"


def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    context["tasks"] = context["tasks"].filter(user=self.request.user)
    context["count"] = context["tasks"].filter(complete=False).count()
    context["projects"] = Project.objects.all()

    search_input = self.request.GET.get("search") or ""
    project_input = self.request.GET.get("project") or ""
    complete_input = self.request.GET.get("complete") or ""

    if search_input:
        context["tasks"] = context["tasks"].filter(title__icontains=search_input)
        context["count"] = context["tasks"].filter(complete=False).count()

    context["search_input"] = search_input

    if project_input:
        context["project_selected"] = project_input
        context["tasks"] = context["tasks"].filter(
            project__name__icontains=project_input
        )
        context["count"] = context["tasks"].filter(complete=False).count()

    if complete_input == "all":
        context["complete_selected"] = complete_input
        context["tasks"] = context["tasks"].filter(user=self.request.user)
        context["count"] = context["tasks"].filter(complete=False).count()
    else:
        if complete_input:
            context["complete_selected"] = complete_input
            context["tasks"] = context["tasks"].filter(
                user=self.request.user, complete=True
            )
            context["count"] = context["tasks"].filter(complete=True).count()
        else:
            context["complete_selected"] = ""
            context["tasks"] = context["tasks"].filter(
                user=self.request.user, complete=False
            )

    # Pagination
    paginated_tasks = Paginator(context["tasks"], 6)
    page_number = self.request.GET.get("page")
    task_page_obj = paginated_tasks.get_page(page_number)

    context["task_page_obj"] = task_page_obj

    get_copy = self.request.GET.copy()
    if get_copy.get("page"):
        get_copy.pop("page")
    context["get_copy"] = get_copy

    return context
 

list.html:

 ...
{% for task in task_page_obj %}
{% empty %}
<p>No tasks to show...</p>
{% endfor %}
<div class="pagination">
<span class="step-links">
    {% if task_page_obj.has_previous %}
        <a href="?page=1amp;{{ get_copy.urlencode }}">amp;laquo; first</a>
        <a href="?page={{ task_page_obj.previous_page_number }}amp;{{ get_copy.urlencode }}">previous</a>
    {% endif %}

    <span class="current">
        Page {{ task_page_obj.number }} of {{ task_page_obj.paginator.num_pages }}.
    </span>

    {% if task_page_obj.has_next %}
        <a href="?page={{ task_page_obj.next_page_number }}amp;{{ get_copy.urlencode }}">next</a>
        <a href="?page={{ task_page_obj.paginator.num_pages }}amp;{{ get_copy.urlencode }}">last amp;raquo;</a>
    {% endif %}
</span>
</div>
 

Ответ №3:

В этом случае вы должны отфильтровать свой набор запросов в функции ниже, и он переопределит набор запросов по умолчанию:

def get_queryset(self): return '<your query>'

ошибка возникает из-за того, что вы пытаетесь изменить не оцененный набор запросов после его фильтрации. и теперь вы можете использовать paginate_by = n , вы не должны получать никаких ошибок.