CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-flask-appbuilder

Simple and rapid application development framework, built on top of Flask, with detailed security, auto CRUD generation, and comprehensive UI components.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

constants-exceptions.mddocs/

Constants and Exception Handling

Framework constants, authentication types, API configuration, and comprehensive exception classes for error handling. This module provides all the constants and exception types used throughout Flask-AppBuilder for configuration, error handling, and system integration.

Capabilities

Authentication Constants

Constants defining supported authentication types and methods for configuring Flask-AppBuilder's security system.

from flask_appbuilder.const import (
    AUTH_OID, AUTH_DB, AUTH_LDAP, AUTH_REMOTE_USER, AUTH_OAUTH
)

# Authentication type constants
AUTH_OID = 0          # OpenID authentication
AUTH_DB = 1           # Database authentication (default)
AUTH_LDAP = 2         # LDAP/Active Directory authentication
AUTH_REMOTE_USER = 3  # Remote user authentication (via headers)
AUTH_OAUTH = 4        # OAuth authentication (Google, GitHub, etc.)

# Usage in application configuration
# Set authentication method in config.py
AUTH_TYPE = AUTH_DB   # Use database authentication

# Database authentication configuration
AUTH_TYPE = AUTH_DB
AUTH_USER_REGISTRATION = True  # Allow user self-registration
AUTH_USER_REGISTRATION_ROLE = "Public"  # Default role for new users

# LDAP authentication configuration
AUTH_TYPE = AUTH_LDAP
AUTH_LDAP_SERVER = "ldap://your-ldap-server.com"
AUTH_LDAP_USE_TLS = True
AUTH_LDAP_BIND_USER = "cn=admin,dc=company,dc=com"
AUTH_LDAP_BIND_PASSWORD = "ldap-password"
AUTH_LDAP_SEARCH = "ou=people,dc=company,dc=com"
AUTH_LDAP_UID_FIELD = "uid"
AUTH_LDAP_FIRSTNAME_FIELD = "givenName" 
AUTH_LDAP_LASTNAME_FIELD = "sn"
AUTH_LDAP_EMAIL_FIELD = "mail"

# OAuth authentication configuration
AUTH_TYPE = AUTH_OAUTH
OAUTH_PROVIDERS = [
    {
        'name': 'google',
        'icon': 'fa-google',
        'token_key': 'access_token',
        'remote_app': {
            'client_id': 'your-google-client-id',
            'client_secret': 'your-google-client-secret',
            'server_metadata_url': 'https://accounts.google.com/.well-known/openid_configuration',
            'client_kwargs': {
                'scope': 'openid email profile'
            }
        }
    },
    {
        'name': 'github', 
        'icon': 'fa-github',
        'token_key': 'access_token',
        'remote_app': {
            'client_id': 'your-github-client-id',
            'client_secret': 'your-github-client-secret',
            'access_token_url': 'https://github.com/login/oauth/access_token',
            'authorize_url': 'https://github.com/login/oauth/authorize',
            'api_base_url': 'https://api.github.com/',
            'client_kwargs': {'scope': 'user:email'}
        }
    }
]

# Remote user authentication configuration
AUTH_TYPE = AUTH_REMOTE_USER
AUTH_REMOTE_USER_VAR = "REMOTE_USER"  # Header containing username

API Constants

Constants for API configuration, versioning, and request/response handling in Flask-AppBuilder's REST API system.

from flask_appbuilder.const import (
    API_SECURITY_VERSION, PERMISSION_PREFIX, API_URI_RIS_KEY, 
    API_RESULT_RIS_KEY, API_ORDER_COLUMNS_RIS_KEY, API_ORDER_DIRECTION_RIS_KEY,
    API_FILTERS_RIS_KEY, API_PAGE_INDEX_RIS_KEY, API_PAGE_SIZE_RIS_KEY,
    API_SELECT_COLUMNS_RIS_KEY, API_SELECT_KEYS_RIS_KEY
)

# API versioning
API_SECURITY_VERSION = "v1"  # Default API version

# Permission system
PERMISSION_PREFIX = "can_"   # Prefix for all permissions ("can_list", "can_show", etc.)

# API request parameter keys (Rison format)
API_URI_RIS_KEY = "q"                    # Main query parameter key
API_RESULT_RIS_KEY = "result"            # Result data key  
API_ORDER_COLUMNS_RIS_KEY = "order_column"      # Ordering column key
API_ORDER_DIRECTION_RIS_KEY = "order_direction"  # Ordering direction key
API_FILTERS_RIS_KEY = "filters"          # Filters array key
API_PAGE_INDEX_RIS_KEY = "page"          # Page index key
API_PAGE_SIZE_RIS_KEY = "page_size"      # Page size key
API_SELECT_COLUMNS_RIS_KEY = "columns"   # Column selection key
API_SELECT_KEYS_RIS_KEY = "keys"         # Primary key selection key

# Usage in API requests
"""
GET /api/v1/person/?q=(filters:!((col:name,opr:ct,value:John)),page:0,page_size:20)

Decoded Rison:
{
  "filters": [{"col": "name", "opr": "ct", "value": "John"}],
  "page": 0, 
  "page_size": 20
}
"""

# API response format constants
API_RESPONSE_SCHEMA = {
    "count": "int",      # Total number of records
    "description_columns": "dict",  # Column descriptions
    "ids": "list",       # List of primary keys
    "label_columns": "dict",        # Column labels
    "list_columns": "list",         # Available list columns
    "order_columns": "list",        # Orderable columns
    "page": "int",       # Current page
    "page_size": "int",  # Items per page
    "result": "list"     # Actual data records
}

# HTTP status codes used by API
API_STATUS_CODES = {
    200: "OK",
    201: "Created", 
    400: "Bad Request",
    401: "Unauthorized",
    403: "Forbidden",
    404: "Not Found",
    422: "Unprocessable Entity",
    500: "Internal Server Error"
}

# API configuration constants
FAB_API_MAX_PAGE_SIZE = 100      # Maximum page size allowed
FAB_API_SWAGGER_UI = True        # Enable Swagger UI
FAB_API_SWAGGER_TEMPLATE = "appbuilder/swagger/swagger.html"
FAB_API_SHOW_STACKTRACE = False  # Show stack traces in API responses

Log Message Constants

Predefined log message templates for consistent logging throughout Flask-AppBuilder applications.

from flask_appbuilder.const import (
    LOGMSG_ERR_SEC_ADD_PERMISSION, LOGMSG_ERR_SEC_ADD_VIEWMENU,
    LOGMSG_ERR_SEC_ADD_PERMVIEW, LOGMSG_ERR_SEC_DEL_PERMISSION,
    LOGMSG_WAR_SEC_LOGIN_FAILED, LOGMSG_WAR_SEC_NO_USER,
    LOGMSG_INF_SEC_ADD_ROLE, LOGMSG_INF_SEC_UPD_ROLE
)

# Security error messages  
LOGMSG_ERR_SEC_ADD_PERMISSION = "Add Permission: {0}"
LOGMSG_ERR_SEC_ADD_VIEWMENU = "Add View Menu Error: {0}"
LOGMSG_ERR_SEC_ADD_PERMVIEW = "Add Permission View Error: {0}"
LOGMSG_ERR_SEC_DEL_PERMISSION = "Del Permission Error: {0}"
LOGMSG_ERR_SEC_ADD_REGISTER_USER = "Add Register User Error: {0}"
LOGMSG_ERR_SEC_UPD_REGISTER_USER = "Update Register User Error: {0}"

# Security warning messages
LOGMSG_WAR_SEC_LOGIN_FAILED = "Login Failed for user: {0}"
LOGMSG_WAR_SEC_NO_USER = "No user yet created, use flask fab create-admin"
LOGMSG_WAR_SEC_NOAPIKEY = "API key not found on request"

# Security info messages  
LOGMSG_INF_SEC_ADD_ROLE = "Added Role: {0}"
LOGMSG_INF_SEC_UPD_ROLE = "Updated Role: {0}"  
LOGMSG_INF_SEC_ADD_PERMISSION = "Added Permission: {0}"
LOGMSG_INF_SEC_ADD_VIEWMENU = "Added View Menu: {0}"
LOGMSG_INF_SEC_ADD_PERMVIEW = "Added Permission View: {0}"

# Database messages
LOGMSG_ERR_DBI_ADD_GENERIC = "Add record error: {0}"
LOGMSG_ERR_DBI_EDIT_GENERIC = "Edit record error: {0}"
LOGMSG_ERR_DBI_DEL_GENERIC = "Delete record error: {0}"

# Usage in custom logging
import logging
from flask_appbuilder.const import LOGMSG_WAR_SEC_LOGIN_FAILED

logger = logging.getLogger(__name__)

def log_failed_login(username):
    logger.warning(LOGMSG_WAR_SEC_LOGIN_FAILED.format(username))

def log_permission_added(permission_name):
    logger.info(LOGMSG_INF_SEC_ADD_PERMISSION.format(permission_name))

Flash Message Constants

Constants for user interface flash messages providing consistent user feedback across Flask-AppBuilder applications.

from flask_appbuilder.const import (
    FLAMSG_ERR_SEC_ACCESS_DENIED, FLAMSG_SUCESSFUL_ADD_RECORD,
    FLAMSG_SUCESSFUL_UPD_RECORD, FLAMSG_SUCESSFUL_DEL_RECORD
)

# Error flash messages
FLAMSG_ERR_SEC_ACCESS_DENIED = "Access is Denied"
FLAMSG_ERR_DBI_ADD_GENERIC = "Add record error. {0}"
FLAMSG_ERR_DBI_EDIT_GENERIC = "Edit record error. {0}"  
FLAMSG_ERR_DBI_DEL_GENERIC = "Delete record error. {0}"

# Success flash messages
FLAMSG_SUCESSFUL_ADD_RECORD = "Added Record"
FLAMSG_SUCESSFUL_UPD_RECORD = "Changed Record"
FLAMSG_SUCESSFUL_DEL_RECORD = "Deleted Record"

# Usage in views
from flask import flash
from flask_appbuilder.const import FLAMSG_SUCESSFUL_ADD_RECORD

class PersonModelView(ModelView):
    def post_add(self, item):
        flash(FLAMSG_SUCESSFUL_ADD_RECORD, "success")
        
    def post_update(self, item):
        flash(FLAMSG_SUCESSFUL_UPD_RECORD, "success")

Exception Classes

Comprehensive exception hierarchy for handling various error conditions in Flask-AppBuilder applications.

from flask_appbuilder.exceptions import (
    FABException, InvalidColumnFilterFABException, InvalidOperationFilterFABException,
    InvalidOrderByColumnFABException, InvalidColumnArgsFABException,
    InterfaceQueryWithoutSession, PasswordComplexityValidationError,
    ApplyFilterException, OAuthProviderUnknown, InvalidLoginAttempt,
    DeleteGroupWithUsersException, DeleteRoleWithUsersException
)

# Base exception class
class FABException(Exception):
    """
    Base Flask-AppBuilder exception class.
    All custom exceptions inherit from this class.
    """
    pass

# Database and model exceptions
class InvalidColumnFilterFABException(FABException):
    """
    Invalid column specified for filtering operation.
    
    Raised when:
    - Column name doesn't exist on model
    - Column is not searchable/filterable
    - Invalid column reference in filter expression
    """
    pass

class InvalidOperationFilterFABException(FABException):
    """
    Invalid filter operation for column type.
    
    Raised when:
    - Using string operations on numeric columns
    - Using numeric operations on string columns  
    - Unsupported filter operation for column type
    """
    pass

class InvalidOrderByColumnFABException(FABException):
    """
    Invalid column specified for ordering.
    
    Raised when:
    - Column name doesn't exist on model
    - Column is not orderable
    - Invalid sort direction specified
    """
    pass

class InvalidColumnArgsFABException(FABException):
    """
    Invalid column arguments provided.
    
    Raised when:
    - Column configuration is malformed
    - Missing required column properties
    - Conflicting column settings
    """
    pass

class InterfaceQueryWithoutSession(FABException):
    """
    Database query attempted without active session.
    
    Raised when:
    - SQLAlchemy session is None
    - Session has been closed
    - Database connection is unavailable
    """
    pass

# Security exceptions
class PasswordComplexityValidationError(FABException):
    """
    Password doesn't meet complexity requirements.
    
    Raised when:
    - Password is too short
    - Password lacks required character types
    - Password fails custom validation rules
    """
    pass

class InvalidLoginAttempt(FABException):
    """
    Login attempt with invalid credentials.
    
    Raised when:
    - Username doesn't exist
    - Password is incorrect
    - Account is disabled
    - Maximum login attempts exceeded
    """
    pass

class OAuthProviderUnknown(FABException):
    """
    Unknown or unconfigured OAuth provider.
    
    Raised when:
    - OAuth provider not in OAUTH_PROVIDERS config
    - Provider configuration is incomplete
    - Provider authentication endpoint unreachable
    """
    pass

# Filter and query exceptions
class ApplyFilterException(FABException):
    """
    Error applying filter to query.
    
    Raised when:
    - Filter syntax is invalid
    - Filter references non-existent columns
    - Filter values are wrong type
    - Database error during filter application
    """
    pass

# User management exceptions
class DeleteGroupWithUsersException(FABException):
    """
    Attempted to delete group that still has users.
    
    Raised when:
    - Group has active user associations
    - CASCADE delete is not enabled
    - Business rules prevent group deletion
    """
    pass

class DeleteRoleWithUsersException(FABException):
    """
    Attempted to delete role that still has users.
    
    Raised when:
    - Role has active user assignments
    - Role is marked as protected/system role
    - CASCADE delete is not enabled
    """
    pass

# Usage examples
def validate_filter_column(self, column_name):
    """Validate that column can be used for filtering."""
    if column_name not in self.search_columns:
        raise InvalidColumnFilterFABException(
            f"Column '{column_name}' is not searchable"
        )

def apply_custom_filter(self, query, filter_spec):
    """Apply custom filter with error handling."""
    try:
        # Apply filter logic
        return query.filter(filter_spec)
    except Exception as e:
        raise ApplyFilterException(
            f"Failed to apply filter: {str(e)}"
        ) from e

def delete_user_role(self, role_id):
    """Delete role with validation."""
    role = self.get_role(role_id)
    
    if role.users:
        raise DeleteRoleWithUsersException(
            f"Cannot delete role '{role.name}' - {len(role.users)} users assigned"
        )
    
    self.delete_role(role)

# Custom exception handling in views
class PersonModelView(ModelView):
    datamodel = SQLAInterface(Person)
    
    def _list(self, **kwargs):
        """Override list with custom error handling."""
        try:
            return super()._list(**kwargs)
        except InvalidColumnFilterFABException as e:
            flash(f"Filter error: {str(e)}", "error")
            return redirect(url_for('PersonModelView.list'))
        except ApplyFilterException as e:
            flash(f"Search error: {str(e)}", "error")
            return redirect(url_for('PersonModelView.list'))

# Exception handling in API
from flask_appbuilder.api import ModelRestApi

class PersonApi(ModelRestApi):
    datamodel = SQLAInterface(Person)
    
    def get_list(self, **kwargs):
        """Override with custom exception handling."""
        try:
            return super().get_list(**kwargs)
        except InvalidColumnFilterFABException as e:
            return self.response_400(f"Invalid filter column: {str(e)}")
        except InvalidOperationFilterFABException as e:
            return self.response_400(f"Invalid filter operation: {str(e)}")
        except ApplyFilterException as e:
            return self.response_500(f"Filter application error: {str(e)}")

# Global exception handler
@app.errorhandler(FABException)
def handle_fab_exception(error):
    """Global handler for Flask-AppBuilder exceptions."""
    logger.error(f"FAB Exception: {str(error)}")
    
    if isinstance(error, InvalidLoginAttempt):
        return redirect(url_for('AuthDBView.login'))
    elif isinstance(error, PasswordComplexityValidationError):
        flash(f"Password error: {str(error)}", "error")
        return redirect(request.referrer or '/')
    else:
        flash(f"Application error: {str(error)}", "error")
        return redirect(url_for('IndexView.index'))

Configuration Constants

Constants for configuring Flask-AppBuilder behavior, templates, and feature flags.

# Flask-AppBuilder configuration constants

# Application configuration
FAB_APP_NAME = "Flask AppBuilder"     # Default app name
FAB_APP_ICON = None                   # App icon path
FAB_APP_THEME = ""                    # Bootstrap theme
FAB_BASE_TEMPLATE = "appbuilder/baselayout.html"  # Base template
FAB_STATIC_FOLDER = "static/appbuilder"           # Static files folder
FAB_STATIC_URL_PATH = "/appbuilder"               # Static URL path

# Security configuration  
FAB_SECURITY_MANAGER_CLASS = "flask_appbuilder.security.manager.SecurityManager"
FAB_UPDATE_PERMS = True               # Auto-update permissions
FAB_PASSWORD_COMPLEXITY_ENABLED = False  # Enable password complexity
FAB_ROLES_MAPPING = {}                # Role name mapping
FAB_AUTH_LIMIT_PER_USER = 0           # Login attempts limit (0 = unlimited)

# API configuration
FAB_API_MAX_PAGE_SIZE = 100           # Maximum API page size
FAB_API_SWAGGER_UI = True             # Enable Swagger UI
FAB_API_SHOW_STACKTRACE = False       # Show stack traces in API errors

# UI configuration  
FAB_ADDON_MANAGERS = []               # List of addon managers
FAB_ADD_SECURITY_VIEWS = True         # Add default security views
FAB_ADD_SECURITY_PERMISSION_VIEW = False  # Add permission management view
FAB_ADD_SECURITY_VIEW_MENU_VIEW = False   # Add view menu management
FAB_ADD_SECURITY_PERMISSION_VIEWS_VIEW = False  # Add permission-view management

# Menu configuration
FAB_MENU_OPEN_SUBMENU_ON_CLICK = True    # Open submenus on click
FAB_ICON_FONT_NAME = "font-awesome"      # Icon font name

# File upload configuration
FAB_UPLOAD_FOLDER = "uploads/"            # Upload directory
FAB_IMG_UPLOAD_FOLDER = "uploads/images/" # Image upload directory
FAB_IMG_UPLOAD_URL = "/uploads/images/"   # Image URL path
FAB_IMG_SIZE = (150, 150, True)          # Image resize dimensions

# Rate limiting
RATELIMIT_ENABLED = False             # Enable rate limiting
RATELIMIT_STORAGE_URL = "memory://"   # Rate limit storage backend
RATELIMIT_HEADERS_ENABLED = True      # Include rate limit headers

# Usage in configuration
class Config:
    # Basic configuration
    FAB_APP_NAME = "My Application"
    FAB_APP_THEME = "cerulean.css"
    FAB_APP_ICON = "/static/img/logo.png"
    
    # Security
    FAB_UPDATE_PERMS = True
    FAB_PASSWORD_COMPLEXITY_ENABLED = True
    FAB_AUTH_LIMIT_PER_USER = 5  # Max 5 login attempts
    
    # API
    FAB_API_MAX_PAGE_SIZE = 50
    FAB_API_SHOW_STACKTRACE = False  # Hide in production
    
    # File uploads
    FAB_UPLOAD_FOLDER = "/var/uploads/"
    FAB_IMG_SIZE = (200, 200, True)  # Larger thumbnails
    
    # Rate limiting
    RATELIMIT_ENABLED = True
    RATELIMIT_STORAGE_URL = "redis://localhost:6379"

# Feature flags
class FeatureFlags:
    """Feature flags for conditional functionality."""
    
    # UI features
    ENABLE_CHARTS = True              # Enable chart views
    ENABLE_FILE_UPLOADS = True        # Enable file upload fields
    ENABLE_BULK_ACTIONS = True        # Enable bulk actions on lists
    ENABLE_EXPORT = True              # Enable data export functionality
    
    # Security features  
    ENABLE_PASSWORD_HISTORY = False   # Track password history
    ENABLE_SESSION_TIMEOUT = True     # Enable session timeout
    ENABLE_LOGIN_AUDIT = True         # Log all login attempts
    
    # API features
    ENABLE_API_RATE_LIMITING = True   # Enable API rate limiting
    ENABLE_API_CACHING = False        # Enable API response caching
    ENABLE_OPENAPI_DOCS = True        # Enable OpenAPI documentation

# Environment-specific constants
class ProductionConfig(Config):
    FAB_API_SHOW_STACKTRACE = False
    RATELIMIT_ENABLED = True
    FAB_PASSWORD_COMPLEXITY_ENABLED = True

class DevelopmentConfig(Config):  
    FAB_API_SHOW_STACKTRACE = True
    RATELIMIT_ENABLED = False
    FAB_UPDATE_PERMS = True

class TestingConfig(Config):
    TESTING = True
    FAB_UPDATE_PERMS = False
    WTF_CSRF_ENABLED = False

Install with Tessl CLI

npx tessl i tessl/pypi-flask-appbuilder

docs

actions-hooks.md

charts.md

cli-tools.md

constants-exceptions.md

core-framework.md

database-models.md

forms-fields.md

index.md

rest-api.md

security.md

views-crud.md

tile.json