In this post you’ll find out how to create a pagination with Jinja macro feature.
Requirements:
- show preconfigured limited number of pages at once
- collapse invisible pages under
...
- provide previous/next navigation buttons
Jinja templates for Bootstrap4
I’ve created 3 tier structure of Jinja templates to use Bootstrap4.
First - bootstrap4_base.html
- loads css and js files from CDN and defines major blocks:
head
- holds content of the<head>
tag and definestitle
,metas
,styles
body
- holds content of the<body>
tag and definesnavbar
,content
,scripts
navbar
- for navigation barcontent
- for boostrap container (tag withclass="container"
)scripts
- goes in the end of the body, here is why
Blocks may be extended or/and overwritten in the later templatesThis template follows Bootstrap4 intro guide
Second - page_base.html
- creates navbar and extends content
block.It shows all the flash messages (the ones invoked with flask flash
function) and adds page_content
.That one will be extended in the last template and holds actual content.
Third - index.html
- will overwrite title
block and extend page_content
Hierarchy of templates is achieved with using of {% extends "<parent_template.html" %}
block to refer to parent template.
To extend a block - scripts
for example - inside child template:
{% block scripts %}
{{ super() }}
<script>
alert(1);
</script>
(% endblock %)
Drop super
to just overwrite it.
Jinja pagination macro
Macros are comparable with functions in regular programming languages. They are useful for putting often used idioms into reusable functions to not repeat yourself (“DRY”). Macro is a bare block that starts with {% macro function_name(formal_params) %}
. It holds the HTML, or rather templated Jinja code that will be reused. I’m putting it’s code into separate file - _macros.html
.
Usage:
- import it with
{% import "_macros.html" as macros %}
- call it with
{{ macros.function_name(actual_params) }}
Bootstrap pagination follows that doc
I’ve tried to put as little logic in the macro itself and do all the calculations in the flask view. My version needs 2 params:
endpoint
- the name of flask endpoint provided tourl_for
which builds the actual link to select pagepages
list if dictionaries, each one has
class
key to define if link is active, normal or disabledpage_label
show the page number or navigation iconshref
- additional param forurl_for
which will hold page
Macro code:
{% macro pagination_widget(pages, endpoint) %}
<nav aria-label="Page navigation example">
<ul class="pagination">
{% for p in pages %}
<li class="page-item {{p['class']}}">
<a href="{{ url_for(endpoint, page = p['href'], **kwargs) }}"
class="page-link"
aria-label={{p['page']}}>
<span aria-hidden="true">{{p['page_label'] | safe}}</span>
<span class="sr-only">{{p['page_label'] | safe}}</span>
</a>
</li>
{% endfor %}
</ul>
</nav>
{% endmacro %}
Flask endpoint
Endpoint code relies on a Pager
class to prepare pages
. It first needs to get the page number from URL parameters. The actual data I’m using is just a list of numbers up to a count
. In real world it’s going to be a query to the database like SELECT column from table LIMIT Y OFFSET X
. Where X - is a page size * current page (zero based), Y - is a page_size.
OFFSET may be slow with big numbers - it’s better to use Sleek mode
Flask endpoint:
@app.route("/")
def index():
page = int(request.args.get('page', 1))
count = 300
data = range(count)
pager = Pager(page, count)
pages = pager.get_pages()
offset = (page - 1) * current_app.config['PAGE_SIZE']
limit = current_app.config['PAGE_SIZE']
data_to_show = data[offset: offset + limit]
return render_template('index.html', pages=pages, data_to_show=data_to_show)
Pager class
This class prepares a pages
list for macro. To do the calculations it needs the number of all items and the current page. Page size and a number of visible pages are read from app config.
Difficult part was to show exactly predefined number of links to another pages. Invisible pages are collapsed under ...
.
App configuration
App config contains two parameters:
PAGE_SIZE
- how many elements show in a pageVISIBLE_PAGE_COUNT
- how may links to pages how in pages including...
s
app = Flask(__name__)
app.secret_key = os.urandom(42)
app.config['PAGE_SIZE'] = 20
app.config['VISIBLE_PAGE_COUNT'] = 10
The whole code is available at GitHub
Do you have another useful macro? Drop me a message on LinkedIn