LuckyB
LuckyB

Reputation: 33

How to display a list across multiple pages in Flask?

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

Answers (4)

Ajax1234
Ajax1234

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

j-richmond
j-richmond

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

epinal
epinal

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

user411133
user411133

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

Related Questions