CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-bootstrap-flask

Bootstrap 4 & 5 helper for Flask projects providing Jinja macros for forms, tables, navigation, and utilities.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

pagination.mddocs/

Pagination

Pagination component rendering for Flask-SQLAlchemy pagination objects with customizable navigation controls, alignment options, URL fragment support, and responsive design considerations.

Capabilities

Full Pagination

Render complete pagination with numbered pages and navigation controls.

def render_pagination(pagination, endpoint=None, prev='«', next='»',
                     size=None, ellipses='…', args={}, fragment='', align=''):
    """
    Render full pagination with numbered pages.
    
    Args:
        pagination: Flask-SQLAlchemy Pagination object
        endpoint (str): Flask endpoint for pagination links (defaults to current endpoint)
        prev (str): Previous button text/HTML (default: '«')
        next (str): Next button text/HTML (default: '»')
        size (str): Pagination size - 'sm' or 'lg' for small/large
        ellipses (str): Text for ellipsis indicator (default: '…')
        args (dict): Additional URL parameters to preserve
        fragment (str): URL fragment (#section) to append to links
        align (str): Alignment - 'center' or 'right' (default: left)
        
    Returns:
        Rendered HTML pagination with Bootstrap styling
        
    Pagination Object Properties Used:
        - page: Current page number
        - pages: Total number of pages
        - per_page: Items per page
        - total: Total number of items
        - has_prev: Boolean for previous page availability
        - has_next: Boolean for next page availability
        - prev_num: Previous page number
        - next_num: Next page number
        - iter_pages(): Iterator for page numbers with gaps
    """

Simple Pager

Render simplified pagination with only Previous/Next buttons.

def render_pager(pagination, fragment='', 
                prev='<span aria-hidden="true">&larr;</span> Previous',
                next='Next <span aria-hidden="true">&rarr;</span>', align=''):
    """
    Render simple pagination pager (Previous/Next only).
    
    Args:
        pagination: Flask-SQLAlchemy Pagination object
        fragment (str): URL fragment to append to links
        prev (str): Previous button text/HTML
        next (str): Next button text/HTML  
        align (str): Alignment - 'center' or 'right'
        
    Returns:
        Rendered HTML pager with Bootstrap styling
        
    Usage:
        - Simpler alternative to full pagination
        - Better for mobile interfaces
        - Suitable when page numbers aren't necessary
    """

Current Page Display

Internal helper for rendering the current active page indicator.

def get_current_page(page):
    """
    Render the current active page in pagination.
    
    Args:
        page (int): Current page number
        
    Returns:
        Rendered HTML for current page indicator
        
    Note: Internal helper used by pagination macros
    """

Pagination Features

Flask-SQLAlchemy Integration

Bootstrap-Flask pagination works seamlessly with Flask-SQLAlchemy's pagination system:

# View function with pagination
@app.route('/posts')
def posts():
    page = request.args.get('page', 1, type=int)
    posts = Post.query.paginate(
        page=page, 
        per_page=5,
        error_out=False
    )
    return render_template('posts.html', posts=posts)

URL Parameter Preservation

Pagination automatically preserves existing URL parameters:

# URL: /search?q=python&category=tutorial&page=2
args = {'q': 'python', 'category': 'tutorial'}

Responsive Design

Pagination components adapt to different screen sizes:

  • Large screens: Full pagination with all page numbers
  • Medium screens: Pagination with ellipses for space
  • Small screens: Consider using simple pager instead

Usage Examples

Basic Pagination

# View function
@app.route('/users')
def users():
    page = request.args.get('page', 1, type=int)
    users = User.query.paginate(
        page=page,
        per_page=10, 
        error_out=False
    )
    return render_template('users.html', users=users)
<!-- Template -->
{% from 'base/pagination.html' import render_pagination %}

<div class="container">
    <!-- Display items -->
    {% for user in users.items %}
        <div class="card mb-2">
            <div class="card-body">
                <h5>{{ user.name }}</h5>
                <p>{{ user.email }}</p>
            </div>
        </div>
    {% endfor %}
    
    <!-- Pagination -->
    {{ render_pagination(users) }}
</div>

Centered Pagination

{{ render_pagination(posts, align='center') }}

Right-Aligned Pagination

{{ render_pagination(products, align='right') }}

Large Pagination

{{ render_pagination(posts, size='lg') }}

Small Pagination

{{ render_pagination(comments, size='sm') }}

Custom Navigation Text

{{ render_pagination(posts, 
    prev='← Previous Posts',
    next='Next Posts →'
) }}

Pagination with URL Parameters

# View preserving search parameters
@app.route('/search')
def search():
    query = request.args.get('q', '')
    category = request.args.get('category', '')
    page = request.args.get('page', 1, type=int)
    
    results = Post.query.filter(
        Post.title.contains(query)
    ).paginate(page=page, per_page=10)
    
    return render_template('search.html', 
                         results=results,
                         search_args={'q': query, 'category': category})
{{ render_pagination(results, args=search_args) }}

Pagination with URL Fragments

<!-- Jump to results section after page change -->
{{ render_pagination(results, fragment='results') }}

<!-- Results section -->
<div id="results">
    <!-- Search results here -->
</div>

Simple Pager Alternative

{% from 'base/pagination.html' import render_pager %}

<!-- Simple Previous/Next pager -->
{{ render_pager(posts) }}

<!-- Centered pager -->
{{ render_pager(posts, align='center') }}

<!-- Custom pager text -->
{{ render_pager(posts, 
    prev='← Older Posts',
    next='Newer Posts →'
) }}

Pagination with Item Count

<div class="d-flex justify-content-between align-items-center mb-3">
    <small class="text-muted">
        Showing {{ posts.per_page * (posts.page - 1) + 1 }} to 
        {{ posts.per_page * posts.page if posts.page < posts.pages else posts.total }}
        of {{ posts.total }} posts
    </small>
    
    {{ render_pagination(posts, align='right') }}
</div>

AJAX Pagination

<!-- Pagination with data attributes for AJAX -->
<div id="pagination-container">
    {{ render_pagination(posts, endpoint='api.posts') }}
</div>

<script>
// Handle pagination clicks with AJAX
$('#pagination-container').on('click', '.page-link', function(e) {
    e.preventDefault();
    const url = $(this).attr('href');
    
    $.get(url, function(data) {
        $('#content').html(data.content);
        $('#pagination-container').html(data.pagination);
    });
});
</script>

Conditional Pagination Display

{% if posts.pages > 1 %}
    <nav aria-label="Posts pagination">
        {{ render_pagination(posts) }}
    </nav>
{% endif %}

Bootstrap 4 vs Bootstrap 5 Differences

The pagination macros work identically across Bootstrap versions, but the underlying CSS classes differ:

Bootstrap 4

  • Uses page-item and page-link classes
  • Active state with active class
  • Disabled state with disabled class

Bootstrap 5

  • Same class structure as Bootstrap 4
  • Enhanced accessibility attributes
  • Improved focus states and keyboard navigation

Advanced Pagination Configuration

{{ render_pagination(posts,
    endpoint='blog.posts',           # Custom endpoint
    prev='‹ Previous',               # Custom previous text
    next='Next ›',                   # Custom next text
    size='lg',                       # Large pagination
    ellipses='...',                  # Custom ellipsis
    align='center',                  # Center alignment
    args={'category': category},     # Preserve URL params
    fragment='posts'                 # Jump to section
) }}

Pagination Info Display

<div class="row align-items-center">
    <div class="col-md-6">
        <small class="text-muted">
            Page {{ posts.page }} of {{ posts.pages }} 
            ({{ posts.total }} total {{ 'post' if posts.total == 1 else 'posts' }})
        </small>
    </div>
    <div class="col-md-6">
        {{ render_pagination(posts, align='right') }}
    </div>
</div>

Per-Page Selection

<div class="d-flex justify-content-between align-items-center mb-3">
    <div>
        <label class="form-label">Items per page:</label>
        <select class="form-select d-inline w-auto" onchange="changePerPage(this.value)">
            <option value="10" {{ 'selected' if posts.per_page == 10 }}>10</option>
            <option value="25" {{ 'selected' if posts.per_page == 25 }}>25</option>
            <option value="50" {{ 'selected' if posts.per_page == 50 }}>50</option>
        </select>
    </div>
    
    {{ render_pagination(posts) }}
</div>

Empty State Handling

{% if posts.items %}
    <!-- Display items -->
    {% for post in posts.items %}
        <!-- Post content -->
    {% endfor %}
    
    <!-- Show pagination only if there are multiple pages -->
    {% if posts.pages > 1 %}
        {{ render_pagination(posts) }}
    {% endif %}
{% else %}
    <div class="text-center py-5">
        <h4>No posts found</h4>
        <p class="text-muted">Try adjusting your search criteria.</p>
    </div>
{% endif %}

Configuration

Pagination respects Flask-SQLAlchemy pagination configuration:

# Flask-SQLAlchemy configuration
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# Pagination defaults
DEFAULT_PER_PAGE = 20
MAX_PER_PAGE = 100

# In view functions
per_page = min(request.args.get('per_page', DEFAULT_PER_PAGE, type=int), MAX_PER_PAGE)

Install with Tessl CLI

npx tessl i tessl/pypi-bootstrap-flask

docs

extension-setup.md

form-rendering.md

index.md

navigation.md

pagination.md

python-utilities.md

table-rendering.md

utilities.md

tile.json