Pagination
This recipe is a useful trick to minimize database access in case of pagination, e.g., when you need to display a list of rows from a database but you want to distribute the rows over multiple pages.
Start by creating a primes application that stores the first 1000 prime numbers in a database.
Here is the model db.py
:
db = DAL('sqlite://primes.db')
db.define_table('prime', Field('value', 'integer'))
def isprime(p):
for i in range(2, p):
if p%i==0: return False
return True
if len(db().select(db.prime.id))==0:
p=2
for i in range(1000):
while not isprime(p): p+=1
db.prime.insert(value=p)
p+=1
Now create an action list_items
in the “default.py” controller that reads like this:
def list_items():
if len(request.args): page=int(request.args[0])
else: page=0
items_per_page=20
limitby=(page*items_per_page, (page+1)*items_per_page+1)
rows=db().select(db.prime.ALL, limitby=limitby)
return dict(rows=rows, page=page, items_per_page=items_per_page)
Notice that this code selects one more item than is needed, 20+1. The extra element tells the view whether there is a next page.
Here is the “default/list_items.html” view:
{{extend 'layout.html'}}
{{for i, row in enumerate(rows):}}
{{if i==items_per_page: break}}
{{=row.value}}<br />
{{pass}}
{{if page:}}
<a href="{{=URL(args=[page-1])}}">previous</a>
{{pass}}
{{if len(rows)>items_per_page:}}
<a href="{{=URL(args=[page+1])}}">next</a>
{{pass}}
In this way we have obtained pagination with one single select per action, and that one select only selects one row more than we need.