Reputation: 2577
I've a small Django project basic-pagination. There is one model, one form and two views (list view, form view). User submits data in the form view, then the data is displayed in the list view. Pagination is enabled to only display 5 posts at a time.
What I've implemented is a form with a GET response to get the data I want to display (e.g. name, date). See code below
class FormListView(ListView):
model = models.ToDoList
paginate_by = 5 # if pagination is desired
template_name = 'pagination/listview.html'
context_object_name = 'forms'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'Form List View'
context['filter_form'] = forms.FilterListView(self.request.GET)
return context
def get_queryset(self):
queryset = models.ToDoList.objects.all().order_by('-id')
name = self.request.GET.get('name')
if name:
queryset = queryset.filter(name=name)
order_by = self.request.GET.get('order_by')
if order_by:
queryset = queryset.order_by(order_by)
print(queryset)
return queryset
The problem is that the class based view ListView
calls the method get_queryset
if you are moving from page 1
to page 2
and thus losing the filtered queryset I wanted.
How can I maintain the filtering throughout the pagination process?
Upvotes: 1
Views: 1813
Reputation: 2577
As @WillemVanOnsem pointed out, the problem is not with the view, but rather with the URLs in the template (templates/pagination/listview.html). Previously, the next button had href="?page={{ page_obj.next_page_number }}"
which meant that request.GET
would only contain the page number for pagination and not the other filter and order by criteria.
The solution was then to append request.GET.urlencode
to href
like
<a class="btn btn-outline-info mb-4" href="?page={{ page_obj.next_page_number }}&{{ request.GET.urlencode }}">Next</a>
However, this is not a total fix because simply appending request.GET
also appends the page number you're currently at. Simply put, if you jump from page 1 to 2 to 3, you will end up with a URL that looks like
http://localhost:8000/listview/?page=1&page=2&page=3...
The request.GET
is a QueryDict like <QueryDict: {'page': ['1'], ...}>
. A solution to this was to simply pop the page
parameter, however, because request.GET
is immutable you first have to make a copy of it. Essentially, I added the following lines to get_context_data
method in the ListVew
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = 'Form List View'
context['filter_form'] = forms.FilterListView(self.request.GET)
get_copy = self.request.GET.copy()
if get_copy.get('page'):
get_copy.pop('page')
context['get_copy'] = get_copy
return context
And in the template I called the get_copy
object like href="?page={{ page_obj.next_page_number }}&{{ get_copy.urlencode }}"
For the entire template example follow templates/pagination/listview.html
Not the most elegant solution, but I feel it's simple enough for most people to utilize.
Upvotes: 3
Reputation: 1
I was with the same problem and I solved it with the link below.
https://www.caktusgroup.com/blog/2018/10/18/filtering-and-pagination-django/
Upvotes: 0