0
# Pagination
1
2
Pagination component rendering for Flask-SQLAlchemy pagination objects with customizable navigation controls, alignment options, URL fragment support, and responsive design considerations.
3
4
## Capabilities
5
6
### Full Pagination
7
8
Render complete pagination with numbered pages and navigation controls.
9
10
```python { .api }
11
def render_pagination(pagination, endpoint=None, prev='«', next='»',
12
size=None, ellipses='…', args={}, fragment='', align=''):
13
"""
14
Render full pagination with numbered pages.
15
16
Args:
17
pagination: Flask-SQLAlchemy Pagination object
18
endpoint (str): Flask endpoint for pagination links (defaults to current endpoint)
19
prev (str): Previous button text/HTML (default: '«')
20
next (str): Next button text/HTML (default: '»')
21
size (str): Pagination size - 'sm' or 'lg' for small/large
22
ellipses (str): Text for ellipsis indicator (default: '…')
23
args (dict): Additional URL parameters to preserve
24
fragment (str): URL fragment (#section) to append to links
25
align (str): Alignment - 'center' or 'right' (default: left)
26
27
Returns:
28
Rendered HTML pagination with Bootstrap styling
29
30
Pagination Object Properties Used:
31
- page: Current page number
32
- pages: Total number of pages
33
- per_page: Items per page
34
- total: Total number of items
35
- has_prev: Boolean for previous page availability
36
- has_next: Boolean for next page availability
37
- prev_num: Previous page number
38
- next_num: Next page number
39
- iter_pages(): Iterator for page numbers with gaps
40
"""
41
```
42
43
### Simple Pager
44
45
Render simplified pagination with only Previous/Next buttons.
46
47
```python { .api }
48
def render_pager(pagination, fragment='',
49
prev='<span aria-hidden="true">←</span> Previous',
50
next='Next <span aria-hidden="true">→</span>', align=''):
51
"""
52
Render simple pagination pager (Previous/Next only).
53
54
Args:
55
pagination: Flask-SQLAlchemy Pagination object
56
fragment (str): URL fragment to append to links
57
prev (str): Previous button text/HTML
58
next (str): Next button text/HTML
59
align (str): Alignment - 'center' or 'right'
60
61
Returns:
62
Rendered HTML pager with Bootstrap styling
63
64
Usage:
65
- Simpler alternative to full pagination
66
- Better for mobile interfaces
67
- Suitable when page numbers aren't necessary
68
"""
69
```
70
71
### Current Page Display
72
73
Internal helper for rendering the current active page indicator.
74
75
```python { .api }
76
def get_current_page(page):
77
"""
78
Render the current active page in pagination.
79
80
Args:
81
page (int): Current page number
82
83
Returns:
84
Rendered HTML for current page indicator
85
86
Note: Internal helper used by pagination macros
87
"""
88
```
89
90
## Pagination Features
91
92
### Flask-SQLAlchemy Integration
93
94
Bootstrap-Flask pagination works seamlessly with Flask-SQLAlchemy's pagination system:
95
96
```python
97
# View function with pagination
98
@app.route('/posts')
99
def posts():
100
page = request.args.get('page', 1, type=int)
101
posts = Post.query.paginate(
102
page=page,
103
per_page=5,
104
error_out=False
105
)
106
return render_template('posts.html', posts=posts)
107
```
108
109
### URL Parameter Preservation
110
111
Pagination automatically preserves existing URL parameters:
112
113
```python
114
# URL: /search?q=python&category=tutorial&page=2
115
args = {'q': 'python', 'category': 'tutorial'}
116
```
117
118
### Responsive Design
119
120
Pagination components adapt to different screen sizes:
121
122
- **Large screens**: Full pagination with all page numbers
123
- **Medium screens**: Pagination with ellipses for space
124
- **Small screens**: Consider using simple pager instead
125
126
## Usage Examples
127
128
### Basic Pagination
129
130
```python
131
# View function
132
@app.route('/users')
133
def users():
134
page = request.args.get('page', 1, type=int)
135
users = User.query.paginate(
136
page=page,
137
per_page=10,
138
error_out=False
139
)
140
return render_template('users.html', users=users)
141
```
142
143
```html
144
<!-- Template -->
145
{% from 'base/pagination.html' import render_pagination %}
146
147
<div class="container">
148
<!-- Display items -->
149
{% for user in users.items %}
150
<div class="card mb-2">
151
<div class="card-body">
152
<h5>{{ user.name }}</h5>
153
<p>{{ user.email }}</p>
154
</div>
155
</div>
156
{% endfor %}
157
158
<!-- Pagination -->
159
{{ render_pagination(users) }}
160
</div>
161
```
162
163
### Centered Pagination
164
165
```html
166
{{ render_pagination(posts, align='center') }}
167
```
168
169
### Right-Aligned Pagination
170
171
```html
172
{{ render_pagination(products, align='right') }}
173
```
174
175
### Large Pagination
176
177
```html
178
{{ render_pagination(posts, size='lg') }}
179
```
180
181
### Small Pagination
182
183
```html
184
{{ render_pagination(comments, size='sm') }}
185
```
186
187
### Custom Navigation Text
188
189
```html
190
{{ render_pagination(posts,
191
prev='← Previous Posts',
192
next='Next Posts →'
193
) }}
194
```
195
196
### Pagination with URL Parameters
197
198
```python
199
# View preserving search parameters
200
@app.route('/search')
201
def search():
202
query = request.args.get('q', '')
203
category = request.args.get('category', '')
204
page = request.args.get('page', 1, type=int)
205
206
results = Post.query.filter(
207
Post.title.contains(query)
208
).paginate(page=page, per_page=10)
209
210
return render_template('search.html',
211
results=results,
212
search_args={'q': query, 'category': category})
213
```
214
215
```html
216
{{ render_pagination(results, args=search_args) }}
217
```
218
219
### Pagination with URL Fragments
220
221
```html
222
<!-- Jump to results section after page change -->
223
{{ render_pagination(results, fragment='results') }}
224
225
<!-- Results section -->
226
<div id="results">
227
<!-- Search results here -->
228
</div>
229
```
230
231
### Simple Pager Alternative
232
233
```html
234
{% from 'base/pagination.html' import render_pager %}
235
236
<!-- Simple Previous/Next pager -->
237
{{ render_pager(posts) }}
238
239
<!-- Centered pager -->
240
{{ render_pager(posts, align='center') }}
241
242
<!-- Custom pager text -->
243
{{ render_pager(posts,
244
prev='← Older Posts',
245
next='Newer Posts →'
246
) }}
247
```
248
249
### Pagination with Item Count
250
251
```html
252
<div class="d-flex justify-content-between align-items-center mb-3">
253
<small class="text-muted">
254
Showing {{ posts.per_page * (posts.page - 1) + 1 }} to
255
{{ posts.per_page * posts.page if posts.page < posts.pages else posts.total }}
256
of {{ posts.total }} posts
257
</small>
258
259
{{ render_pagination(posts, align='right') }}
260
</div>
261
```
262
263
### AJAX Pagination
264
265
```html
266
<!-- Pagination with data attributes for AJAX -->
267
<div id="pagination-container">
268
{{ render_pagination(posts, endpoint='api.posts') }}
269
</div>
270
271
<script>
272
// Handle pagination clicks with AJAX
273
$('#pagination-container').on('click', '.page-link', function(e) {
274
e.preventDefault();
275
const url = $(this).attr('href');
276
277
$.get(url, function(data) {
278
$('#content').html(data.content);
279
$('#pagination-container').html(data.pagination);
280
});
281
});
282
</script>
283
```
284
285
### Conditional Pagination Display
286
287
```html
288
{% if posts.pages > 1 %}
289
<nav aria-label="Posts pagination">
290
{{ render_pagination(posts) }}
291
</nav>
292
{% endif %}
293
```
294
295
### Bootstrap 4 vs Bootstrap 5 Differences
296
297
The pagination macros work identically across Bootstrap versions, but the underlying CSS classes differ:
298
299
#### Bootstrap 4
300
- Uses `page-item` and `page-link` classes
301
- Active state with `active` class
302
- Disabled state with `disabled` class
303
304
#### Bootstrap 5
305
- Same class structure as Bootstrap 4
306
- Enhanced accessibility attributes
307
- Improved focus states and keyboard navigation
308
309
### Advanced Pagination Configuration
310
311
```html
312
{{ render_pagination(posts,
313
endpoint='blog.posts', # Custom endpoint
314
prev='‹ Previous', # Custom previous text
315
next='Next ›', # Custom next text
316
size='lg', # Large pagination
317
ellipses='...', # Custom ellipsis
318
align='center', # Center alignment
319
args={'category': category}, # Preserve URL params
320
fragment='posts' # Jump to section
321
) }}
322
```
323
324
### Pagination Info Display
325
326
```html
327
<div class="row align-items-center">
328
<div class="col-md-6">
329
<small class="text-muted">
330
Page {{ posts.page }} of {{ posts.pages }}
331
({{ posts.total }} total {{ 'post' if posts.total == 1 else 'posts' }})
332
</small>
333
</div>
334
<div class="col-md-6">
335
{{ render_pagination(posts, align='right') }}
336
</div>
337
</div>
338
```
339
340
### Per-Page Selection
341
342
```html
343
<div class="d-flex justify-content-between align-items-center mb-3">
344
<div>
345
<label class="form-label">Items per page:</label>
346
<select class="form-select d-inline w-auto" onchange="changePerPage(this.value)">
347
<option value="10" {{ 'selected' if posts.per_page == 10 }}>10</option>
348
<option value="25" {{ 'selected' if posts.per_page == 25 }}>25</option>
349
<option value="50" {{ 'selected' if posts.per_page == 50 }}>50</option>
350
</select>
351
</div>
352
353
{{ render_pagination(posts) }}
354
</div>
355
```
356
357
### Empty State Handling
358
359
```html
360
{% if posts.items %}
361
<!-- Display items -->
362
{% for post in posts.items %}
363
<!-- Post content -->
364
{% endfor %}
365
366
<!-- Show pagination only if there are multiple pages -->
367
{% if posts.pages > 1 %}
368
{{ render_pagination(posts) }}
369
{% endif %}
370
{% else %}
371
<div class="text-center py-5">
372
<h4>No posts found</h4>
373
<p class="text-muted">Try adjusting your search criteria.</p>
374
</div>
375
{% endif %}
376
```
377
378
## Configuration
379
380
Pagination respects Flask-SQLAlchemy pagination configuration:
381
382
```python
383
# Flask-SQLAlchemy configuration
384
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
385
386
# Pagination defaults
387
DEFAULT_PER_PAGE = 20
388
MAX_PER_PAGE = 100
389
390
# In view functions
391
per_page = min(request.args.get('per_page', DEFAULT_PER_PAGE, type=int), MAX_PER_PAGE)
392
```