I have a working table generated by django-tables2:
my_filter = TestFilter(request.POST)
table = TestTable(TestObj.objects.all(), order_by="-my_date")
RequestConfig(request, paginate={"per_page": 10}).configure(table)
return render(request, 'test_app/index.html', {'table': table, 'my_filter': my_filter})
The above code returns a table with hundreds of objects that are neatly paginated with 10 items per page. When I click "Next" at the bottom of the table, pagination works well and I can navigate through different pages. However, I noticed the following behavior:
my_filter
which displays a subset of the original unfiltered tablemy_filter
again displays the 2nd page of the filtered tableI would like the filter to persist while navigating different pages. I found a similar question here. That solution indicates that the html code needs to be altered. However, in my case django-tables2 is generating the html.
How can I correctly implement pagination with filtering using django-tables2?
-Update-
I've tried using GET instead of POST:
if request.method == 'GET':
my_filter = TestFilter(request.GET)
my_choice = my_filter.data['my_choice']
table = TestTable(TestObj.objects.filter(choice=my_choice), order_by="-my_date")
RequestConfig(request, paginate={"per_page": 10}).configure(table)
return render(request, 'test_app/index.html', {'table': table, 'my_filter': my_filter})
My template:
<form action="" method="get"> {% csrf_token %}
{{ my_filter }} <input type="submit" value="Apply Filter"/>
</form>
This results in a KeyError due to my_choice
not existing in GET. As a result the page does not even load.
django-tables2 simplifies the task of turning sets of data into HTML tables. It has native support for pagination and sorting. It does for HTML tables what django. forms does for HTML forms.
Django provides a few classes that help you manage paginated data – that is, data that's split across several pages, with “Previous/Next” links. These classes live in django/core/paginator.py. For examples, see the Pagination topic guide.
Django-filter is a generic, reusable application to alleviate writing some of the more mundane bits of view code. Specifically, it allows users to filter down a queryset based on a model's fields, displaying the form to let them do this.
Which version of django_tables2 are you using? I checked the source and saw that django_tables2 is using a template tag named querystring
for creating the pagination links in the table.html
template. the querystring
tag updates the current url with the paging parameters. so django_tables2 supports pagination + filtering out of the box (that's what I remembered).
Please try updating to the latest version of django_tables2 and make sure that you are using the default table.html
template for rendering your tables.
Also are you submitting your filter form with GET or POST? Please make sure that submit it with GET!
Finally, please take a look at my answer to this question Django Tables - Column Filtering
update: I took a closer look in the code you posted: first of all, you are passing post data to the filter: you cannot use POST for that, POST has to be used only for actions that modify your data. also I saw that you do not filter anything but instead you pass .all() to the table! where is the actual filtering done? you should pass the filtered data to the table as I describe in the answer above!
update 2:
The problem with your view is that when you first visit the page the GET
dictionary does not contain the my_choice
attribute so it will throw an exception when trying to access the my_choice
attribute through the []
operator, so you should check to see if it actually exists using for instance .get()
, something like this:
my_filter = TestFilter(request.GET)
my_choice = my_filter.data.get('my_choice') # This won't throw an exception
if my_choice: # If my_choice existed on the GET dictionary this will return non-null value
table = TestTable(TestObj.objects.filter(choice=my_choice), order_by="-my_date")
else:
table = TestTable(TestObj.objects.all(), order_by="-my_date")
RequestConfig(request, paginate={"per_page": 10}).configure(table)
return render(request, 'test_app/index.html', {'table': table, 'my_filter': my_filter})
The above should work, however by doing do queryset filtering yourself - you are violating nearly every django design philosophy !
That's why I told you to read my other answer to the similar question (Django Tables - Column Filtering) in which I recommend using django-filter which is a package explicitly used for filtering querysets. Please check the documentation or my answer to see how it can be used (I'd be happy to help if you have questions).
Also, there is a number of other minor problems with your code:
You don't need to check if the request.method
is GET
- it will always be GET
since you won't do any POST
s
You shouldn't include {{ csrf_token }}
to your template - it is only needed for POST
.
The TestFilter
class is actually a Form
that's why I recommend naming it TestFilterForm
or something similar -- if you'd used django-filter then you'd create a FilterSet
class which'd be named TestFilter
. Correct naming of classes is very important, when I first saw your code I thought that the TestFilter
class was a FilterSet
and not a Form
!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With