Reputation: 33
In Flask, I am trying to display a dynamic list of items across multiple pages. The size of the list can possibly reach over 1000.
In app.py
I have a route such as:
@app.route('/displayitems')
def displayitems():
item_list = ['item1', 'item2', 'item3'] # Up to 1000 items
return render_template('displayitems.html', item_list=item_list)
Then in displayitems.html
i have:
{% for item in itemList%}
<li class="list-group-item">
<div class="item_number">{{ loop.index }}</div>
<div class="item_name">{{ item }}</div>
</li>
{% endfor %}
Which lists all items on the same page. However, I would like to somehow have, say, the first 20 items on one page, then be able to click a "next" button to view the next 20 items on another page, and so on for all of the items in the list. Does anyone know the best way to go about doing this?
Upvotes: 1
Views: 5446
Reputation: 71471
First, you will want to create a pagination object to pass to the wrapped route method's render_template
:
class PageResult:
def __init__(self, data, page = 1, number = 20):
self.__dict__ = dict(zip(['data', 'page', 'number'], [data, page, number]))
self.full_listing = [self.data[i:i+number] for i in range(0, len(self.data), number)]
def __iter__(self):
for i in self.full_listing[self.page-1]:
yield i
def __repr__(self): #used for page linking
return "/displayitems/{}".format(self.page+1) #view the next page
@app.route('/displayitems/<pagenum>')
def displayitems(pagenum):
item_list = ['item1', 'item2', 'item3']
return render_template('displayitems.html', listing = PageResult(item_list, pagenum))
Then, in your template, create the pagination with bootstrap and current listing:
<ul>
{%for item in listing%}
<li>{{item}}</li>
{%endfor%}
</ul>
<ul class="pagination">
{%if listing.page == 1%}
<li class="page-item disabled"><a class="page-link" href="#">Previous</a></li>
{%else%}
<li class="page-item"><a class="page-link" href="/displayitems/{{listing.page-1}}">Previous</a></li>
{%endif%}
<li class="page-item"><a class="page-link" href="{{listing}}">Next</a></li>
</ul>
Upvotes: 6
Reputation: 11
I needed to do something similar. I found a simple way after watching the Flask Tutorial of Corey Schafer about Pagination. I needed to change it a bit, since I don't make use of a database (with inbuilt function for paginations). The way I coded this is as follows:
@app.route('/home')
def home(): #this doesn't make a request
#pagination
current_page = request.args.get('page', 1, type=int)
#the variable page will store the (integer) part after '?page=' from the url
#http://localhost:5000/?page=1
#http://localhost:5000/?page=2
#pages, per page
items_per_page= 20
pages = round(len(item_list)/items_per_page+ .499) #the .499 is for rounding to the upside
from_page = int(page) * items_per_page - items_per_page #36 per page
upto_page = int(page) * items_per_page
list_part = item_list[from_page:upto_page]
return render_template('displayitems.html', list_part=list_part, pages=pages, current_page=current_page)
So instead of giving the complete list item_list=item_list
, you give a part of that list list_part=list_part
. To render pagination in the template:
{% for page_num in range(1, pages) %}
{% if page_num in [1, 2, 3, current_page, current_page-1, current_page+1, pages-3, pages-2, pages-1] %}
{% if current_page == page_num %}
<a class="btn btn-info mb-4" href="{{ url_for('home', page=page_num) }}">{{ page_num }}</a>
{% else %}
<a class="btn btn-outline-info mb-4" href="{{ url_for('home', page=page_num) }}">{{ page_num }}</a>
{% endif %}
{% elif current_page > 4 and page_num == 4 %}
....
{% elif current_page < pages-4 and page_num == pages-4 %}
....
{% endif %}
{% endfor %}
I didn't make use of next/prev buttons (I will add this when I have a solution and time for this). But even a long list is nicely styled, with ellipses to show that there are more pages but not show all the pages. The button of the active page has a different class, to show on which page you are on.
You can go quickly to a page by changing the integer after ?page= in the url: http://localhost:5000/?page=1
EDIT: To add next/prev buttons, put outside the for loop the following Flask if blocks.
{% if current_page != 1 %}
<a class="btn btn-outline-info mb-4" href="{{ url_for('home', page=current_page-1) }}"><<</a>
{% endif %}
{% for page_num in range(1, pages) %}
...........[SAME PART AS ABOVE]..........
{% endfor %}
{% if current_page != pages %}
<a class="btn btn-outline-info mb-4" href="{{ url_for('home', page=current_page+1) }}">>></a>
{% endif %}
Upvotes: 0
Reputation: 1465
You can do something like this:
@app.route('/displayitems/<page>/')
def displayitems(page=1):
items_per_page = 20
item_list = ['item1', 'item2', 'item3', etc..]
start_pos = 0 if page == 1 else items_per_page * (page - 1)
return render_template('displayitems.html', item_list=item_list[start_pos: start_pos + items_per_page])
Call http://your_domain/displayitems/1/
when your page loads, then call it increasing the page number every time the "next" button is clicked.
NOTE:
Make sure start_pos
and start_pos + items_per_page
< len(item_list)
or you'll get an exception.
Upvotes: 0
Reputation: 361
You're looking for pagination functionality, which isn't directly included in the Flask framework. You should look into using something like Flask-paginate
Upvotes: 0