I've created View which filters data by search query given in textbox. As well as I used Paginator to show data divided into pages.
My problem is, when I filter data with Q object then and try to paginate by clicking the next button, all data is refreshed.
When I search text by Q object the URL becomes http://127.0.0.1:8000/mael/parties/?q=keyword
And from clicking the next button the URL becomes http://127.0.0.1:8000/mael/parties/?page=2
When I manually change URL http://127.0.0.1:8000/mael/parties/?q=keyword&page=2, then it works. But I don't know how to do this in code.
Is it possible to use Q object search and pagination together?
My View
from mael.models import PartyTotalBillsView
from django.views.generic import ListView
from django.db.models import Q
from django.http import HttpResponseRedirect
class PartyListView(ListView):
paginate_by = 2
model = PartyTotalBillsView
def parties(request):
# Show all records or searched query record
search_text = request.GET.get('q','')
try:
if search_text:
queryset = (Q(party_name__icontains=search_text))
party_list = PartyTotalBillsView.objects.filter(queryset).order_by('party_name')
else:
# Show all data if empty keyword is entered
party_list = PartyTotalBillsView.objects.order_by('party_name')
except PartyTotalBillsView.DoesNotExist:
party_list = None
paginator = Paginator(party_list, 2) # Show 2 rows per page: for Test
page_number = request.GET.get('page')
party_list = paginator.get_page(page_number)
return render(request, 'mael/parties.html', {'party_list': party_list})
Template file
<form id="search-form" method="get" action="/mael/parties/">
<input id="search-text" type="text" name="q" placeholder="Enter search keyword">
<input class="btn-search-party" type="submit" value="Search" />
</form>
<br/>
<table class="show-data">
<thead>
<tr>
<th>ID</th>
<th>Party Name</th>
<th>Total Bill Amount</th>
<th>Phone</th>
<th>Address</th>
<th></th>
</tr>
</thead>
{% if party_list %}
<tbody>
{% for party in party_list %}
<tr>
<td class="party-id">{{ party.party_id }}</td>
<td class="party-name">{{ party.party_name }}</td>
<td>{{ party.total_bills }}</td>
<td class="party-phone">{{ party.party_phone }}</td>
<td class="party-address">{{ party.party_address }}</td>
<td>
<button class="btn-modify" data-partyid="{{party.party_id}}" type="buttton">
Modify
</button>
</td>
</tr>
{% endfor %}
</tbody>
{% endif %}
</table>
<div class="pagination">
<span class="step-links">
{% if party_list.has_previous %}
<a href="?page=1">« first</a>
<a href="?page={{ party_list.previous_page_number }}">previous</a>
{% endif %}
<span class="current">
Page {{ party_list.number }} of {{ party_list.paginator.num_pages }}
</span>
{% if party_list.has_next %}
<a href="?page={{ party_list.next_page_number }}">next</a>
<a href="?page={{ party_list.paginator.num_pages }}">last »</a>
{% endif %}
</span>
</div>
Please do not use two views. A ListView
can perform filtering as well:
class PartyListView(ListView):
paginate_by = 2
model = PartyTotalBillsView
template_name = 'mael/parties.html'
context_object_name = 'party_list'
def querystring(self):
qs = self.request.GET.copy()
qs.pop(self.page_kwarg, None)
return qs.urlencode()
def get_queryset(self):
qs = super().get_queryset()
if 'q' in self.request.GET:
qs = qs.filter(party_name__icontains=self.request.GET['q'])
return qs.order_by('party_name')
In the links for the previous and next pages, you then append the querystring
of the view:
<span class="step-links">
{% if party_list.has_previous %}
<a href="?page=1&{{ view.querystring }}">« first</a>
<a href="?page={{ page_obj.previous_page_number }}&{{ view.querystring }}">previous</a>
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
</span>
{% if party_list.has_next %}
<a href="?page={{ page_obj.next_page_number }}&{{ view.querystring }}">next</a>
<a href="?page={{ page_obj.paginator.num_pages }}&{{ view.querystring }}">last »</a>
{% endif %}
</span>