Reputation: 57
I have a Django form that takes input values from users. The values are then used in making query to a table ResourceBase
, which finally returns a list of filtered results.
Since the results might be a long list, I added a pagination function with "Prev" and "Next" buttons. My problem is that when I click "Prev" or "Next" button, the form gets restored into default values. And all returned results are all gone. How do I prevent this from happening?
I think the form gets reset because of "form1 = QueryForm()" when a request is not "POST". However I just have difficulty coming up with a neat solution since I'm new to Django and web dev.
In views.py:
def search(request):
if request.method == "POST":
form1 = QueryForm(data=request.POST)
layer_dict = []
if form1.is_valid():
inp_ct = form1.cleaned_data['country']
q1 = ResourceBase.objects.filter(country_name__iexact=inp_ct)
for layer in q1:
down_url = 'xxxxxxx'.format(layer.title)
view_url = 'xxxxxxx'.format(layer.title)
layer_dict.append((layer.title, down_url, view_url))
layer_dict = sorted(layer_dict, key = lambda x:x[0])
paginator = Paginator(layer_dict, 10)
page = request.GET.get('page', 1)
try:
layers = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
layers = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
layers = paginator.page(paginator.num_pages)
context = {'form1': form1, 'layers': layers}
else:
form1 = QueryForm()
context = {'form1': form1}
return render(request, 'my_app/search.html', context)
In search.html:
<br />
<h3>Pagination Test</h3>
<br /><br/>
<div class="row">
<div class="col-md-4">
<form method="POST">
{% csrf_token %}
<div class="form-controls">
{{ form1|as_bootstrap }}
</div>
<button class="btn btn-primary" type="submit" style="float: right;" title = "Click to search" ><i class="fa fa-search"></i></button>
</form>
<form method="GET">
<button class="btn btn-primary" type="submit" value="Reset" name="Reset" title="Reset all choices">Reset</button>
</form>
</div>
</div>
{% if layers %}
<div class="row">
<div class="col-md-8">
<div id = "search_results" >
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Select</th>
<th scope="col">Layer Name</th>
<th scope="col">Download</th>
<th scope="col">View Layer</th>
</tr>
</thead>
<tbody>
{% for layer in layers %}
<tr>
<td><input class= messageCheckbox type="checkbox" name="checks" value="{{layer.1}}"/></td>
<td>{{layer.0}}</td>
<td><a href="{{layer.1}}" target="_blank"> Download Layer </a></td>
<td><input class="btn btn-primary" onclick="window.open('{{layer.2}}')" id="view" type="button" name="view" value="View"></td>
</tr>
{% endfor %}
<tr>
<td><input type="checkbox" onClick="toggle(this, 'checks')"/> Select All</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<button class="btn btn-primary" type="button" name="download" style="float: left;" onClick= "open_all_links();">Download Selected</button>
</div>
<div class="a_pagination" align="right">
<span class="step-links">
{% if layers.has_previous %}
<a class="btn btn-primary btn-sm" name="prev_page" href="?page={{ layers.previous_page_number }}" role="button">Prev.</a>
{% endif %}
<span class="current" style ="color:#2C689C;font-size:16px;padding:8px;">
page {{ layers.number }} of {{ layers.paginator.num_pages }}
</span>
{% if layers.has_next %}
<a class= "btn btn-primary btn-sm" href="?page={{ layers.next_page_number }}" role="button">Next</a>
{% endif %}
</span>
</div>
</div>
</div>
{% endif %}
<script type="text/javascript" >
.......
</script>
Upvotes: 2
Views: 2115
Reputation: 2467
You can change the pagination links into buttons to submit the form. More detailed answer in here: How to define which input fields the form has by pressing different buttons?
You can read my comment on the answer. It explains how the answer can be helpful in pagination.
I modified the pagination code I found in https://simpleisbetterthancomplex.com/series/2017/10/09/a-complete-beginners-guide-to-django-part-6.html The modified code for pagination is also as below:
<nav aria-label="Topics pagination" class="mb-4">
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item">
<button form="my_form" name="page" value="{{page_obj.number|add:'-1'}}" role="button" class="btn btn-link">Previous</button>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">Previous</span>
</li>
{% endif %}
{% for i in page_obj.paginator.page_range %}
{% if page_obj.number == i %}
<li class="page-item active">
<span class="page-link">
{{ i }}
<span class="sr-only">(current)</span>
</span>
</li>
{% else %}
<li class="page-item">
<button form="my_form" name="page" value="{{i}}" role="button" class="btn btn-link">{{ i }}</button>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<button form="my_form" name="page" value="{{page_obj.number|add:1}}" role="button" class="btn btn-link">Next</button>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">Next</span>
</li>
{% endif %}
</ul>
</nav>>
Upvotes: 1
Reputation: 1298
You don't need to use POST Method to pass your arguments to your views.py
.
Follow the below example and rewrite your view
and your html form
.
here a simple form for user to enter the word for search:
<form method="get" action="">
<input type="text" name="search4" class="search_input" placeholder="Search" required="required">
<input type="submit" value="Search">
</form>
The next step is that you should check the input in your views.py
, we named the input tage name="search4" so we check if there is any input in our form using this code in our views.py
:
from django.db.models import Q
from django.core.paginator import Paginator
def search(request):
query = request.GET.get("search4")
if query:
queryset = ResourceBase.objects.objects.all() # this will get all of your object of your model
results = queryset.filter(Q(country_name__iexact=query)).all()
number_of_objects = results.count() # get the exact number of object to show in your html file
paginator = Paginator(results, 12) # Show 12 contacts per page
page_var = 'page' # this will use for pagination in your html file
page = request.GET.get(page_var) # this will use for pagination in your html file
contacts = paginator.get_page(page) # send only 12 object to your html file to show to user
context = {
"items": contacts,
"key": str(query),
'page': page_var,
"number_of_objects": number_of_objects,
}
return render(request=request, template_name='search.html', context=context, content_type=None, status=None,
using=None)
else:
... # if user didn't enter anything to search
After getting and searching the user input in your data base, You should show it to user in your search.html
file like this:
{% for item in items %}
<div>
<div>
<div class="product_title">{{ item.title }}</div> # show the part that you want the users to see
... # rest of your item parts to show
</div>
</div>
{% endfor %}
<div class="pagination">
<span class="step-links">
{% if items.has_previous %} # check the pagination that if there is perivious pages
<a href="?{{ page }}=1">« first</a>
<a href="?{{ page }}={{ items.previous_page_number }}">previous</a>
{% endif %}
<span class="current">
Page {{ items.number }} of {{ items.paginator.num_pages }} # example of result : Page 1 of 13
</span>
{% if items.has_next %}
<a href="?{{ page }}={{ items.next_page_number }}"</a> # check the pagination that if there is any next or perivious pages
<a href="?{{ page }}={{ items.paginator.num_pages }}">last »</a> # a link to last page
{% endif %}
</span>
{{ pagination }}
this is a basic search page with Paginator, if you need any further help or question, I will be happy to help.
Upvotes: 3
Reputation: 2478
The code <a class= "btn btn-primary btn-sm" href="?page={{ layers.next_page_number }}" role="button">Next</a>
will indeed GET the page and the form1 = QueryForm()
code will result in empty form. You are on a right track here.
You have two options:
1) Change the next/prev buttons so that they are inside the form1 form and they POST stuff. It might be challenging to move them inside the same form tag.
If you target modern browsers you can use HTML5 form tag in submit (https://www.w3schools.com/tags/att_button_form.asp).
<form method="POST" id="form1">
{{ form1|as_bootstrap }}
</form>
... outside the form tag, then
<button class="btn btn-primary btn-sm" form="form1" name="next" value="{{ layers.next_page_number }}" role="button">Next</button>
You should have in request.POST
the next value.
2) Initialize the QueryForm from GET params.
form1 = QueryForm(data=request.GET)
and include the form parameters into the url. For this you would need some Javascript (for example How to use an input field as query parameter to a destination?) as Django doesn't know about the values in the form on rendering time before user inserts them.
Upvotes: 2