Bootstrap 4 & 5 helper for Flask projects providing Jinja macros for forms, tables, navigation, and utilities.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Data table rendering with Bootstrap styling, supporting CRUD actions, responsive design, pagination integration, and flexible column formatting options for displaying data collections.
Render data collections as Bootstrap-styled tables with optional CRUD actions and extensive customization.
def render_table(data, titles=None, primary_key='id', primary_key_title='#',
caption=None, table_classes=None, header_classes=None,
body_classes=None, responsive=False, responsive_class='table-responsive',
safe_columns=None, urlize_columns=None, model=None,
show_actions=False, actions_title='Actions', custom_actions=None,
view_url=None, edit_url=None, delete_url=None, new_url=None,
action_pk_placeholder=':id'):
"""
Render a data collection as a Bootstrap table.
Args:
data (list): List of objects/dicts to display in table
titles (list): List of (field_name, display_title) tuples for columns
primary_key (str): Primary key field name (default: 'id')
primary_key_title (str): Display title for primary key column (default: '#')
caption (str): Table caption text
table_classes (str): CSS classes for table element
header_classes (str): CSS classes for table header
body_classes (str): CSS classes for table body
responsive (bool): Enable responsive table wrapper
responsive_class (str): CSS class for responsive wrapper
safe_columns (list): Column names to render as safe HTML
urlize_columns (list): Column names to convert URLs to links
model: SQLAlchemy model class for automatic title detection
show_actions (bool): Display CRUD action buttons
actions_title (str): Title for actions column
custom_actions (list): Custom action button definitions
view_url (str): URL pattern for view action
edit_url (str): URL pattern for edit action
delete_url (str): URL pattern for delete action
new_url (str): URL for new record creation
action_pk_placeholder (str): Placeholder for primary key in URLs
Returns:
Rendered HTML table with Bootstrap styling
Data Sources:
- List of dictionaries
- List of SQLAlchemy model instances
- List of custom objects with attributes
URL Patterns:
- Use :id placeholder for primary key substitution
- Example: '/users/:id/edit' becomes '/users/123/edit'
"""Automatically generate table column titles from SQLAlchemy models.
def get_table_titles(data, primary_key, primary_key_title):
"""
Detect and build table titles from SQLAlchemy ORM objects.
Args:
data (list): List of SQLAlchemy model instances
primary_key (str): Primary key field name
primary_key_title (str): Display title for primary key
Returns:
list: List of (field_name, display_title) tuples
Behavior:
- Extracts column names from SQLAlchemy model metadata
- Converts snake_case to Title Case (user_name -> User Name)
- Excludes fields starting with underscore
- Sets primary key title as specified
Example:
For User model with columns: id, first_name, last_name, email
Returns: [('id', '#'), ('first_name', 'First Name'),
('last_name', 'Last Name'), ('email', 'Email')]
"""Internal helper for constructing action URLs with dynamic parameters.
def build_url(record, endpoint, url_tuples, model, pk_field):
"""
Internal helper to build URLs for table actions.
Args:
record: Data record (dict or object)
endpoint (str): Flask endpoint or URL pattern
url_tuples (list): URL parameter tuples
model: SQLAlchemy model class
pk_field (str): Primary key field name
Returns:
str: Constructed URL with substituted parameters
Note: This is an internal helper used by render_table
"""Bootstrap-Flask tables support multiple data source types:
data = [
{'id': 1, 'name': 'John Doe', 'email': 'john@example.com'},
{'id': 2, 'name': 'Jane Smith', 'email': 'jane@example.com'},
]users = User.query.all() # List of SQLAlchemy model instancesclass Person:
def __init__(self, id, name, email):
self.id = id
self.name = name
self.email = email
data = [Person(1, 'John', 'john@example.com')]titles = [
('id', '#'),
('first_name', 'First Name'),
('last_name', 'Last Name'),
('email', 'Email Address'),
('created_at', 'Date Created')
]# For SQLAlchemy models - automatically generates titles
titles = get_table_titles(users, 'id', '#')Bootstrap-Flask provides built-in CRUD action buttons:
# URL patterns with :id placeholder
view_url = '/users/:id'
edit_url = '/users/:id/edit'
delete_url = '/users/:id/delete'
new_url = '/users/new'custom_actions = [
{
'title': 'Activate',
'url': '/users/:id/activate',
'classes': 'btn-success btn-sm'
},
{
'title': 'Archive',
'url': '/users/:id/archive',
'classes': 'btn-warning btn-sm'
}
]Render column content as safe HTML without escaping:
safe_columns = ['description', 'notes'] # Won't escape HTML tagsAutomatically convert URLs to clickable links:
urlize_columns = ['website', 'blog_url'] # Convert to <a> tagsTables can be made responsive to handle overflow on small screens:
# Enable responsive wrapper
responsive = True
responsive_class = 'table-responsive-md' # Responsive on medium screens and below# View function
@app.route('/users')
def users():
users = [
{'id': 1, 'name': 'John Doe', 'email': 'john@example.com'},
{'id': 2, 'name': 'Jane Smith', 'email': 'jane@example.com'},
]
return render_template('users.html', users=users)<!-- Template -->
{% from 'base/table.html' import render_table %}
<div class="container">
<h2>Users</h2>
{{ render_table(users) }}
</div>{{ render_table(users,
table_classes="table table-striped table-hover table-bordered",
header_classes="table-dark",
caption="List of registered users"
) }}{{ render_table(users,
show_actions=True,
view_url="/users/:id",
edit_url="/users/:id/edit",
delete_url="/users/:id/delete",
new_url="/users/new",
actions_title="Operations"
) }}# Model
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(50))
last_name = db.Column(db.String(50))
email = db.Column(db.String(120))
created_at = db.Column(db.DateTime)
# View
@app.route('/users')
def users():
users = User.query.all()
titles = get_table_titles(users, 'id', 'User ID')
return render_template('users.html', users=users, titles=titles){{ render_table(users, titles=titles, model=User, show_actions=True) }}{{ render_table(products,
titles=[
('id', 'ID'),
('name', 'Product Name'),
('description', 'Description'),
('price', 'Price'),
('website', 'Website')
],
responsive=True,
responsive_class="table-responsive-lg",
safe_columns=['description'],
urlize_columns=['website'],
table_classes="table table-sm"
) }}{{ render_table(orders,
show_actions=True,
custom_actions=[
{
'title': 'Ship',
'url': '/orders/:id/ship',
'classes': 'btn-success btn-sm'
},
{
'title': 'Cancel',
'url': '/orders/:id/cancel',
'classes': 'btn-danger btn-sm'
},
{
'title': 'Invoice',
'url': '/orders/:id/invoice',
'classes': 'btn-info btn-sm'
}
]
) }}# View with pagination
@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){% from 'base/table.html' import render_table %}
{% from 'base/pagination.html' import render_pagination %}
{{ render_table(users.items, show_actions=True) }}
{{ render_pagination(users) }}{{ render_table(data,
titles=custom_titles,
primary_key='user_id',
primary_key_title='User ID',
table_classes="table table-striped table-hover",
header_classes="table-primary",
body_classes="small",
responsive=True,
responsive_class="table-responsive-md",
safe_columns=['bio', 'notes'],
urlize_columns=['website', 'linkedin'],
show_actions=True,
actions_title="Manage",
view_url="/users/:id/profile",
edit_url="/users/:id/settings",
delete_url="/admin/users/:id/delete",
new_url="/users/register",
action_pk_placeholder=':id'
) }}{% if data %}
{{ render_table(data) }}
{% else %}
<div class="alert alert-info">
<h4>No Records Found</h4>
<p>There are currently no records to display.</p>
<a href="{{ url_for('new_record') }}" class="btn btn-primary">
Add New Record
</a>
</div>
{% endif %}Table rendering respects several Flask configuration variables:
# Table action button titles (configurable)
app.config['BOOTSTRAP_TABLE_VIEW_TITLE'] = 'View'
app.config['BOOTSTRAP_TABLE_EDIT_TITLE'] = 'Edit'
app.config['BOOTSTRAP_TABLE_DELETE_TITLE'] = 'Delete'
app.config['BOOTSTRAP_TABLE_NEW_TITLE'] = 'New'These configuration values are used as default titles for CRUD action buttons and can be overridden at the template level.
Install with Tessl CLI
npx tessl i tessl/pypi-bootstrap-flask