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

python-utilities.mddocs/

Python Utilities

Helper functions for form field detection, table title generation, template error handling, and custom field components that extend Bootstrap-Flask functionality.

Capabilities

Form Field Detection

Utility function for identifying hidden form fields in templates.

def is_hidden_field_filter(field) -> bool:
    """
    Check if a WTForms field is a hidden field.
    
    Args:
        field: WTForms field object
        
    Returns:
        bool: True if field is an instance of HiddenField, False otherwise
        
    Usage:
        - Used as Jinja filter: bootstrap_is_hidden_field
        - Registered globally in Jinja environment
        - Helps templates conditionally render hidden fields
        
    Example:
        {% for field in form %}
            {% if not bootstrap_is_hidden_field(field) %}
                {{ render_field(field) }}
            {% endif %}
        {% endfor %}
    """

Table Title Generation

Automatically generate human-readable table column titles from SQLAlchemy model metadata.

def get_table_titles(data, primary_key, primary_key_title) -> list:
    """
    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 column
        
    Returns:
        list: List of (field_name, display_title) tuples
        
    Behavior:
        - Extracts column names from first object's __table__.columns
        - Converts snake_case to Title Case (user_name → User Name)
        - Excludes fields starting with underscore (private fields)
        - Sets primary key column to specified title
        - Returns empty list if data is empty
        
    Requirements:
        - Only works with SQLAlchemy ORM objects
        - Objects must have __table__ attribute with columns
        
    Example:
        User model columns: id, first_name, last_name, email, _password_hash
        Returns: [('id', 'User ID'), ('first_name', 'First Name'), 
                 ('last_name', 'Last Name'), ('email', 'Email')]
    """

Template Error Handling

Helper function for raising runtime errors from within Jinja templates.

def raise_helper(message) -> None:
    """
    Raise a RuntimeError from within templates.
    
    Args:
        message (str): Error message to display
        
    Raises:
        RuntimeError: Always raises with provided message
        
    Usage:
        - Registered as 'raise' in Jinja globals
        - Allows templates to throw errors for debugging
        - Useful for template development and error handling
        
    Template Usage:
        {% if not required_variable %}
            {{ raise('Required variable is missing') }}
        {% endif %}
    """

Switch Field Component

Custom WTForms field that renders as a Bootstrap switch instead of a checkbox.

class SwitchField(BooleanField):
    """
    A wrapper field for BooleanField that renders as a Bootstrap switch.
    
    Inherits from WTForms BooleanField with identical functionality
    but renders with Bootstrap switch styling instead of checkbox.
    
    Attributes:
        All BooleanField attributes and methods available
        
    Bootstrap Versions:
        - Bootstrap 4: Uses custom-switch classes
        - Bootstrap 5: Uses form-switch classes
    """
    
    def __init__(self, label=None, **kwargs):
        """
        Initialize switch field.
        
        Args:
            label (str, optional): Field label text
            **kwargs: Additional arguments passed to BooleanField
            
        Supported kwargs:
            - validators: List of validation functions
            - default: Default value (True/False)
            - description: Help text for field
            - render_kw: Additional HTML attributes
        """

Global Template Variables

Bootstrap-Flask registers several Python utilities as Jinja global variables during extension initialization.

# Available in all templates after Bootstrap extension initialization
bootstrap_is_hidden_field   # is_hidden_field_filter function
get_table_titles            # Table title generation function
warn                        # warnings.warn function for deprecation notices  
raise                       # raise_helper function for template errors
bootstrap                   # Extension instance with load_css(), load_js(), etc.

Usage Examples

Hidden Field Detection

# Form definition
class ContactForm(FlaskForm):
    name = StringField('Name', validators=[DataRequired()])
    email = StringField('Email', validators=[DataRequired(), Email()])
    csrf_token = HiddenField()  # CSRF protection
    honeypot = HiddenField()    # Bot detection
    message = TextAreaField('Message', validators=[DataRequired()])
    submit = SubmitField('Send Message')
<!-- Template - render only visible fields -->
<form method="post">
    {% for field in form %}
        {% if not bootstrap_is_hidden_field(field) and field.type != 'SubmitField' %}
            <div class="mb-3">
                {{ field.label(class="form-label") }}
                {{ field(class="form-control") }}
                {% if field.errors %}
                    {% for error in field.errors %}
                        <div class="text-danger">{{ error }}</div>
                    {% endfor %}
                {% endif %}
            </div>
        {% endif %}
    {% endfor %}
    
    <!-- Render hidden fields separately -->
    {% for field in form %}
        {% if bootstrap_is_hidden_field(field) %}
            {{ field() }}
        {% endif %}
    {% endfor %}
    
    {{ form.submit(class="btn btn-primary") }}
</form>

Automatic Table Titles

# SQLAlchemy model
class Product(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    product_name = db.Column(db.String(100), nullable=False)
    unit_price = db.Column(db.Numeric(10, 2))
    in_stock = db.Column(db.Boolean, default=True)
    category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    _internal_code = db.Column(db.String(50))  # Excluded (starts with _)

# View function
@app.route('/products')
def products():
    products = Product.query.all()
    
    # Automatically generate titles
    titles = get_table_titles(products, 'id', 'Product ID')
    # Returns: [('id', 'Product ID'), ('product_name', 'Product Name'), 
    #          ('unit_price', 'Unit Price'), ('in_stock', 'In Stock'),
    #          ('category_id', 'Category Id'), ('created_at', 'Created At')]
    
    return render_template('products.html', products=products, titles=titles)
<!-- Template with auto-generated titles -->
{% from 'base/table.html' import render_table %}

{{ render_table(products, titles=titles) }}

Custom Table Titles

# Override auto-generated titles with custom ones
@app.route('/products')
def products():
    products = Product.query.all()
    
    # Custom titles for better display
    titles = [
        ('id', '#'),
        ('product_name', 'Product'),
        ('unit_price', 'Price ($)'),
        ('in_stock', 'Available'),
        ('created_at', 'Date Added')
    ]
    
    return render_template('products.html', products=products, titles=titles)

Switch Field Usage

# Form with switch fields
class UserPreferencesForm(FlaskForm):
    email_notifications = SwitchField('Email Notifications')
    sms_notifications = SwitchField('SMS Notifications') 
    dark_mode = SwitchField('Dark Mode')
    auto_save = SwitchField('Auto-save Documents')
    public_profile = SwitchField('Public Profile')
    submit = SubmitField('Save Preferences')
<!-- Template rendering switches -->
{% from 'bootstrap5/form.html' import render_field %}

<form method="post">
    {{ form.hidden_tag() }}
    
    <div class="mb-4">
        <h5>Notification Settings</h5>
        {{ render_field(form.email_notifications) }}
        {{ render_field(form.sms_notifications) }}
    </div>
    
    <div class="mb-4">
        <h5>Interface Settings</h5>
        {{ render_field(form.dark_mode) }}
        {{ render_field(form.auto_save) }}
    </div>
    
    <div class="mb-4">
        <h5>Privacy Settings</h5>
        {{ render_field(form.public_profile) }}
    </div>
    
    {{ render_field(form.submit) }}
</form>

Template Error Handling

<!-- Debug template with error checking -->
{% if not user %}
    {{ raise('User object is required but not provided') }}
{% endif %}

{% if not user.permissions %}
    {{ raise('User permissions not loaded') }}
{% endif %}

<!-- Development mode template validation -->
{% if config.DEBUG %}
    {% if not posts %}
        {{ raise('Posts data missing in template context') }}
    {% endif %}
    
    {% if posts|length == 0 %}
        <!-- This won't raise an error, just show message -->
        <p>No posts available</p>
    {% endif %}
{% endif %}

Complex Field Detection

# Custom form field detection logic
class AdvancedForm(FlaskForm):
    # Regular fields
    title = StringField('Title')
    content = TextAreaField('Content')
    
    # Hidden fields
    csrf_token = HiddenField()
    user_id = HiddenField()
    
    # Special fields
    remember_me = BooleanField('Remember Me')
    auto_publish = SwitchField('Auto-publish')
    
    submit = SubmitField('Save')
<!-- Template with advanced field handling -->
<form method="post">
    <!-- Hidden fields first -->
    {% for field in form if bootstrap_is_hidden_field(field) %}
        {{ field() }}
    {% endfor %}
    
    <!-- Regular input fields -->
    {% for field in form if not bootstrap_is_hidden_field(field) and field.type not in ['BooleanField', 'SwitchField', 'SubmitField'] %}
        {{ render_field(field) }}
    {% endfor %}
    
    <!-- Boolean and switch fields in a group -->
    <div class="mb-3">
        <label class="form-label">Options</label>
        {% for field in form if field.type in ['BooleanField', 'SwitchField'] %}
            {{ render_field(field) }}
        {% endfor %}
    </div>
    
    <!-- Submit button -->
    {% for field in form if field.type == 'SubmitField' %}
        {{ render_field(field) }}
    {% endfor %}
</form>

Utility Function Chaining

# View combining multiple utilities
@app.route('/admin/users')
def admin_users():
    users = User.query.all()
    
    # Generate titles and customize
    titles = get_table_titles(users, 'id', '#')
    
    # Customize specific titles
    title_dict = dict(titles)
    title_dict['is_active'] = 'Status'
    title_dict['last_login'] = 'Last Seen'
    titles = list(title_dict.items())
    
    return render_template('admin/users.html', users=users, titles=titles)

Integration with Flask-Admin

# Using utilities with Flask-Admin
from flask_admin.contrib.sqla import ModelView

class UserAdmin(ModelView):
    def on_model_change(self, form, model, is_created):
        # Use raise_helper for validation in admin
        if not model.email:
            raise_helper('Email is required for user accounts')
        
        super().on_model_change(form, model, is_created)
    
    def scaffold_list_columns(self):
        # Use get_table_titles for admin list view
        columns = super().scaffold_list_columns()
        # Customize column display based on auto-generated titles
        return columns

Form Validation with Utilities

# Custom validator using utilities
def validate_required_fields(form):
    """Validate that all non-hidden fields have values"""
    errors = []
    
    for field in form:
        if not is_hidden_field_filter(field) and hasattr(field, 'data'):
            if field.data is None or field.data == '':
                errors.append(f'{field.label.text} is required')
    
    return errors

# Usage in view
@app.route('/submit', methods=['POST'])
def submit_form():
    form = MyForm()
    
    if form.validate_on_submit():
        # Additional validation
        validation_errors = validate_required_fields(form)
        if validation_errors:
            for error in validation_errors:
                flash(error, 'error')
            return render_template('form.html', form=form)
        
        # Process form
        return redirect(url_for('success'))
    
    return render_template('form.html', form=form)

Best Practices

Field Detection

  • Use bootstrap_is_hidden_field consistently for hidden field detection
  • Always render hidden fields separately from visible fields
  • Include CSRF tokens and other security fields as hidden

Table Titles

  • Use get_table_titles for initial title generation
  • Customize generated titles for better user experience
  • Consider internationalization when overriding titles

Switch Fields

  • Use for boolean settings and preferences
  • Prefer switches over checkboxes for on/off toggles
  • Group related switches together in forms

Error Handling

  • Use raise_helper for development debugging only
  • Remove debug raises before production deployment
  • Consider logging instead of raising in production templates

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