or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

actions-hooks.mdcharts.mdcli-tools.mdconstants-exceptions.mdcore-framework.mddatabase-models.mdforms-fields.mdindex.mdrest-api.mdsecurity.mdviews-crud.md

constants-exceptions.mddocs/

0

# Constants and Exception Handling

1

2

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.

3

4

## Capabilities

5

6

### Authentication Constants

7

8

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

9

10

```python { .api }

11

from flask_appbuilder.const import (

12

AUTH_OID, AUTH_DB, AUTH_LDAP, AUTH_REMOTE_USER, AUTH_OAUTH

13

)

14

15

# Authentication type constants

16

AUTH_OID = 0 # OpenID authentication

17

AUTH_DB = 1 # Database authentication (default)

18

AUTH_LDAP = 2 # LDAP/Active Directory authentication

19

AUTH_REMOTE_USER = 3 # Remote user authentication (via headers)

20

AUTH_OAUTH = 4 # OAuth authentication (Google, GitHub, etc.)

21

22

# Usage in application configuration

23

# Set authentication method in config.py

24

AUTH_TYPE = AUTH_DB # Use database authentication

25

26

# Database authentication configuration

27

AUTH_TYPE = AUTH_DB

28

AUTH_USER_REGISTRATION = True # Allow user self-registration

29

AUTH_USER_REGISTRATION_ROLE = "Public" # Default role for new users

30

31

# LDAP authentication configuration

32

AUTH_TYPE = AUTH_LDAP

33

AUTH_LDAP_SERVER = "ldap://your-ldap-server.com"

34

AUTH_LDAP_USE_TLS = True

35

AUTH_LDAP_BIND_USER = "cn=admin,dc=company,dc=com"

36

AUTH_LDAP_BIND_PASSWORD = "ldap-password"

37

AUTH_LDAP_SEARCH = "ou=people,dc=company,dc=com"

38

AUTH_LDAP_UID_FIELD = "uid"

39

AUTH_LDAP_FIRSTNAME_FIELD = "givenName"

40

AUTH_LDAP_LASTNAME_FIELD = "sn"

41

AUTH_LDAP_EMAIL_FIELD = "mail"

42

43

# OAuth authentication configuration

44

AUTH_TYPE = AUTH_OAUTH

45

OAUTH_PROVIDERS = [

46

{

47

'name': 'google',

48

'icon': 'fa-google',

49

'token_key': 'access_token',

50

'remote_app': {

51

'client_id': 'your-google-client-id',

52

'client_secret': 'your-google-client-secret',

53

'server_metadata_url': 'https://accounts.google.com/.well-known/openid_configuration',

54

'client_kwargs': {

55

'scope': 'openid email profile'

56

}

57

}

58

},

59

{

60

'name': 'github',

61

'icon': 'fa-github',

62

'token_key': 'access_token',

63

'remote_app': {

64

'client_id': 'your-github-client-id',

65

'client_secret': 'your-github-client-secret',

66

'access_token_url': 'https://github.com/login/oauth/access_token',

67

'authorize_url': 'https://github.com/login/oauth/authorize',

68

'api_base_url': 'https://api.github.com/',

69

'client_kwargs': {'scope': 'user:email'}

70

}

71

}

72

]

73

74

# Remote user authentication configuration

75

AUTH_TYPE = AUTH_REMOTE_USER

76

AUTH_REMOTE_USER_VAR = "REMOTE_USER" # Header containing username

77

```

78

79

### API Constants

80

81

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

82

83

```python { .api }

84

from flask_appbuilder.const import (

85

API_SECURITY_VERSION, PERMISSION_PREFIX, API_URI_RIS_KEY,

86

API_RESULT_RIS_KEY, API_ORDER_COLUMNS_RIS_KEY, API_ORDER_DIRECTION_RIS_KEY,

87

API_FILTERS_RIS_KEY, API_PAGE_INDEX_RIS_KEY, API_PAGE_SIZE_RIS_KEY,

88

API_SELECT_COLUMNS_RIS_KEY, API_SELECT_KEYS_RIS_KEY

89

)

90

91

# API versioning

92

API_SECURITY_VERSION = "v1" # Default API version

93

94

# Permission system

95

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

96

97

# API request parameter keys (Rison format)

98

API_URI_RIS_KEY = "q" # Main query parameter key

99

API_RESULT_RIS_KEY = "result" # Result data key

100

API_ORDER_COLUMNS_RIS_KEY = "order_column" # Ordering column key

101

API_ORDER_DIRECTION_RIS_KEY = "order_direction" # Ordering direction key

102

API_FILTERS_RIS_KEY = "filters" # Filters array key

103

API_PAGE_INDEX_RIS_KEY = "page" # Page index key

104

API_PAGE_SIZE_RIS_KEY = "page_size" # Page size key

105

API_SELECT_COLUMNS_RIS_KEY = "columns" # Column selection key

106

API_SELECT_KEYS_RIS_KEY = "keys" # Primary key selection key

107

108

# Usage in API requests

109

"""

110

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

111

112

Decoded Rison:

113

{

114

"filters": [{"col": "name", "opr": "ct", "value": "John"}],

115

"page": 0,

116

"page_size": 20

117

}

118

"""

119

120

# API response format constants

121

API_RESPONSE_SCHEMA = {

122

"count": "int", # Total number of records

123

"description_columns": "dict", # Column descriptions

124

"ids": "list", # List of primary keys

125

"label_columns": "dict", # Column labels

126

"list_columns": "list", # Available list columns

127

"order_columns": "list", # Orderable columns

128

"page": "int", # Current page

129

"page_size": "int", # Items per page

130

"result": "list" # Actual data records

131

}

132

133

# HTTP status codes used by API

134

API_STATUS_CODES = {

135

200: "OK",

136

201: "Created",

137

400: "Bad Request",

138

401: "Unauthorized",

139

403: "Forbidden",

140

404: "Not Found",

141

422: "Unprocessable Entity",

142

500: "Internal Server Error"

143

}

144

145

# API configuration constants

146

FAB_API_MAX_PAGE_SIZE = 100 # Maximum page size allowed

147

FAB_API_SWAGGER_UI = True # Enable Swagger UI

148

FAB_API_SWAGGER_TEMPLATE = "appbuilder/swagger/swagger.html"

149

FAB_API_SHOW_STACKTRACE = False # Show stack traces in API responses

150

```

151

152

### Log Message Constants

153

154

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

155

156

```python { .api }

157

from flask_appbuilder.const import (

158

LOGMSG_ERR_SEC_ADD_PERMISSION, LOGMSG_ERR_SEC_ADD_VIEWMENU,

159

LOGMSG_ERR_SEC_ADD_PERMVIEW, LOGMSG_ERR_SEC_DEL_PERMISSION,

160

LOGMSG_WAR_SEC_LOGIN_FAILED, LOGMSG_WAR_SEC_NO_USER,

161

LOGMSG_INF_SEC_ADD_ROLE, LOGMSG_INF_SEC_UPD_ROLE

162

)

163

164

# Security error messages

165

LOGMSG_ERR_SEC_ADD_PERMISSION = "Add Permission: {0}"

166

LOGMSG_ERR_SEC_ADD_VIEWMENU = "Add View Menu Error: {0}"

167

LOGMSG_ERR_SEC_ADD_PERMVIEW = "Add Permission View Error: {0}"

168

LOGMSG_ERR_SEC_DEL_PERMISSION = "Del Permission Error: {0}"

169

LOGMSG_ERR_SEC_ADD_REGISTER_USER = "Add Register User Error: {0}"

170

LOGMSG_ERR_SEC_UPD_REGISTER_USER = "Update Register User Error: {0}"

171

172

# Security warning messages

173

LOGMSG_WAR_SEC_LOGIN_FAILED = "Login Failed for user: {0}"

174

LOGMSG_WAR_SEC_NO_USER = "No user yet created, use flask fab create-admin"

175

LOGMSG_WAR_SEC_NOAPIKEY = "API key not found on request"

176

177

# Security info messages

178

LOGMSG_INF_SEC_ADD_ROLE = "Added Role: {0}"

179

LOGMSG_INF_SEC_UPD_ROLE = "Updated Role: {0}"

180

LOGMSG_INF_SEC_ADD_PERMISSION = "Added Permission: {0}"

181

LOGMSG_INF_SEC_ADD_VIEWMENU = "Added View Menu: {0}"

182

LOGMSG_INF_SEC_ADD_PERMVIEW = "Added Permission View: {0}"

183

184

# Database messages

185

LOGMSG_ERR_DBI_ADD_GENERIC = "Add record error: {0}"

186

LOGMSG_ERR_DBI_EDIT_GENERIC = "Edit record error: {0}"

187

LOGMSG_ERR_DBI_DEL_GENERIC = "Delete record error: {0}"

188

189

# Usage in custom logging

190

import logging

191

from flask_appbuilder.const import LOGMSG_WAR_SEC_LOGIN_FAILED

192

193

logger = logging.getLogger(__name__)

194

195

def log_failed_login(username):

196

logger.warning(LOGMSG_WAR_SEC_LOGIN_FAILED.format(username))

197

198

def log_permission_added(permission_name):

199

logger.info(LOGMSG_INF_SEC_ADD_PERMISSION.format(permission_name))

200

```

201

202

### Flash Message Constants

203

204

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

205

206

```python { .api }

207

from flask_appbuilder.const import (

208

FLAMSG_ERR_SEC_ACCESS_DENIED, FLAMSG_SUCESSFUL_ADD_RECORD,

209

FLAMSG_SUCESSFUL_UPD_RECORD, FLAMSG_SUCESSFUL_DEL_RECORD

210

)

211

212

# Error flash messages

213

FLAMSG_ERR_SEC_ACCESS_DENIED = "Access is Denied"

214

FLAMSG_ERR_DBI_ADD_GENERIC = "Add record error. {0}"

215

FLAMSG_ERR_DBI_EDIT_GENERIC = "Edit record error. {0}"

216

FLAMSG_ERR_DBI_DEL_GENERIC = "Delete record error. {0}"

217

218

# Success flash messages

219

FLAMSG_SUCESSFUL_ADD_RECORD = "Added Record"

220

FLAMSG_SUCESSFUL_UPD_RECORD = "Changed Record"

221

FLAMSG_SUCESSFUL_DEL_RECORD = "Deleted Record"

222

223

# Usage in views

224

from flask import flash

225

from flask_appbuilder.const import FLAMSG_SUCESSFUL_ADD_RECORD

226

227

class PersonModelView(ModelView):

228

def post_add(self, item):

229

flash(FLAMSG_SUCESSFUL_ADD_RECORD, "success")

230

231

def post_update(self, item):

232

flash(FLAMSG_SUCESSFUL_UPD_RECORD, "success")

233

```

234

235

### Exception Classes

236

237

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

238

239

```python { .api }

240

from flask_appbuilder.exceptions import (

241

FABException, InvalidColumnFilterFABException, InvalidOperationFilterFABException,

242

InvalidOrderByColumnFABException, InvalidColumnArgsFABException,

243

InterfaceQueryWithoutSession, PasswordComplexityValidationError,

244

ApplyFilterException, OAuthProviderUnknown, InvalidLoginAttempt,

245

DeleteGroupWithUsersException, DeleteRoleWithUsersException

246

)

247

248

# Base exception class

249

class FABException(Exception):

250

"""

251

Base Flask-AppBuilder exception class.

252

All custom exceptions inherit from this class.

253

"""

254

pass

255

256

# Database and model exceptions

257

class InvalidColumnFilterFABException(FABException):

258

"""

259

Invalid column specified for filtering operation.

260

261

Raised when:

262

- Column name doesn't exist on model

263

- Column is not searchable/filterable

264

- Invalid column reference in filter expression

265

"""

266

pass

267

268

class InvalidOperationFilterFABException(FABException):

269

"""

270

Invalid filter operation for column type.

271

272

Raised when:

273

- Using string operations on numeric columns

274

- Using numeric operations on string columns

275

- Unsupported filter operation for column type

276

"""

277

pass

278

279

class InvalidOrderByColumnFABException(FABException):

280

"""

281

Invalid column specified for ordering.

282

283

Raised when:

284

- Column name doesn't exist on model

285

- Column is not orderable

286

- Invalid sort direction specified

287

"""

288

pass

289

290

class InvalidColumnArgsFABException(FABException):

291

"""

292

Invalid column arguments provided.

293

294

Raised when:

295

- Column configuration is malformed

296

- Missing required column properties

297

- Conflicting column settings

298

"""

299

pass

300

301

class InterfaceQueryWithoutSession(FABException):

302

"""

303

Database query attempted without active session.

304

305

Raised when:

306

- SQLAlchemy session is None

307

- Session has been closed

308

- Database connection is unavailable

309

"""

310

pass

311

312

# Security exceptions

313

class PasswordComplexityValidationError(FABException):

314

"""

315

Password doesn't meet complexity requirements.

316

317

Raised when:

318

- Password is too short

319

- Password lacks required character types

320

- Password fails custom validation rules

321

"""

322

pass

323

324

class InvalidLoginAttempt(FABException):

325

"""

326

Login attempt with invalid credentials.

327

328

Raised when:

329

- Username doesn't exist

330

- Password is incorrect

331

- Account is disabled

332

- Maximum login attempts exceeded

333

"""

334

pass

335

336

class OAuthProviderUnknown(FABException):

337

"""

338

Unknown or unconfigured OAuth provider.

339

340

Raised when:

341

- OAuth provider not in OAUTH_PROVIDERS config

342

- Provider configuration is incomplete

343

- Provider authentication endpoint unreachable

344

"""

345

pass

346

347

# Filter and query exceptions

348

class ApplyFilterException(FABException):

349

"""

350

Error applying filter to query.

351

352

Raised when:

353

- Filter syntax is invalid

354

- Filter references non-existent columns

355

- Filter values are wrong type

356

- Database error during filter application

357

"""

358

pass

359

360

# User management exceptions

361

class DeleteGroupWithUsersException(FABException):

362

"""

363

Attempted to delete group that still has users.

364

365

Raised when:

366

- Group has active user associations

367

- CASCADE delete is not enabled

368

- Business rules prevent group deletion

369

"""

370

pass

371

372

class DeleteRoleWithUsersException(FABException):

373

"""

374

Attempted to delete role that still has users.

375

376

Raised when:

377

- Role has active user assignments

378

- Role is marked as protected/system role

379

- CASCADE delete is not enabled

380

"""

381

pass

382

383

# Usage examples

384

def validate_filter_column(self, column_name):

385

"""Validate that column can be used for filtering."""

386

if column_name not in self.search_columns:

387

raise InvalidColumnFilterFABException(

388

f"Column '{column_name}' is not searchable"

389

)

390

391

def apply_custom_filter(self, query, filter_spec):

392

"""Apply custom filter with error handling."""

393

try:

394

# Apply filter logic

395

return query.filter(filter_spec)

396

except Exception as e:

397

raise ApplyFilterException(

398

f"Failed to apply filter: {str(e)}"

399

) from e

400

401

def delete_user_role(self, role_id):

402

"""Delete role with validation."""

403

role = self.get_role(role_id)

404

405

if role.users:

406

raise DeleteRoleWithUsersException(

407

f"Cannot delete role '{role.name}' - {len(role.users)} users assigned"

408

)

409

410

self.delete_role(role)

411

412

# Custom exception handling in views

413

class PersonModelView(ModelView):

414

datamodel = SQLAInterface(Person)

415

416

def _list(self, **kwargs):

417

"""Override list with custom error handling."""

418

try:

419

return super()._list(**kwargs)

420

except InvalidColumnFilterFABException as e:

421

flash(f"Filter error: {str(e)}", "error")

422

return redirect(url_for('PersonModelView.list'))

423

except ApplyFilterException as e:

424

flash(f"Search error: {str(e)}", "error")

425

return redirect(url_for('PersonModelView.list'))

426

427

# Exception handling in API

428

from flask_appbuilder.api import ModelRestApi

429

430

class PersonApi(ModelRestApi):

431

datamodel = SQLAInterface(Person)

432

433

def get_list(self, **kwargs):

434

"""Override with custom exception handling."""

435

try:

436

return super().get_list(**kwargs)

437

except InvalidColumnFilterFABException as e:

438

return self.response_400(f"Invalid filter column: {str(e)}")

439

except InvalidOperationFilterFABException as e:

440

return self.response_400(f"Invalid filter operation: {str(e)}")

441

except ApplyFilterException as e:

442

return self.response_500(f"Filter application error: {str(e)}")

443

444

# Global exception handler

445

@app.errorhandler(FABException)

446

def handle_fab_exception(error):

447

"""Global handler for Flask-AppBuilder exceptions."""

448

logger.error(f"FAB Exception: {str(error)}")

449

450

if isinstance(error, InvalidLoginAttempt):

451

return redirect(url_for('AuthDBView.login'))

452

elif isinstance(error, PasswordComplexityValidationError):

453

flash(f"Password error: {str(error)}", "error")

454

return redirect(request.referrer or '/')

455

else:

456

flash(f"Application error: {str(error)}", "error")

457

return redirect(url_for('IndexView.index'))

458

```

459

460

### Configuration Constants

461

462

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

463

464

```python { .api }

465

# Flask-AppBuilder configuration constants

466

467

# Application configuration

468

FAB_APP_NAME = "Flask AppBuilder" # Default app name

469

FAB_APP_ICON = None # App icon path

470

FAB_APP_THEME = "" # Bootstrap theme

471

FAB_BASE_TEMPLATE = "appbuilder/baselayout.html" # Base template

472

FAB_STATIC_FOLDER = "static/appbuilder" # Static files folder

473

FAB_STATIC_URL_PATH = "/appbuilder" # Static URL path

474

475

# Security configuration

476

FAB_SECURITY_MANAGER_CLASS = "flask_appbuilder.security.manager.SecurityManager"

477

FAB_UPDATE_PERMS = True # Auto-update permissions

478

FAB_PASSWORD_COMPLEXITY_ENABLED = False # Enable password complexity

479

FAB_ROLES_MAPPING = {} # Role name mapping

480

FAB_AUTH_LIMIT_PER_USER = 0 # Login attempts limit (0 = unlimited)

481

482

# API configuration

483

FAB_API_MAX_PAGE_SIZE = 100 # Maximum API page size

484

FAB_API_SWAGGER_UI = True # Enable Swagger UI

485

FAB_API_SHOW_STACKTRACE = False # Show stack traces in API errors

486

487

# UI configuration

488

FAB_ADDON_MANAGERS = [] # List of addon managers

489

FAB_ADD_SECURITY_VIEWS = True # Add default security views

490

FAB_ADD_SECURITY_PERMISSION_VIEW = False # Add permission management view

491

FAB_ADD_SECURITY_VIEW_MENU_VIEW = False # Add view menu management

492

FAB_ADD_SECURITY_PERMISSION_VIEWS_VIEW = False # Add permission-view management

493

494

# Menu configuration

495

FAB_MENU_OPEN_SUBMENU_ON_CLICK = True # Open submenus on click

496

FAB_ICON_FONT_NAME = "font-awesome" # Icon font name

497

498

# File upload configuration

499

FAB_UPLOAD_FOLDER = "uploads/" # Upload directory

500

FAB_IMG_UPLOAD_FOLDER = "uploads/images/" # Image upload directory

501

FAB_IMG_UPLOAD_URL = "/uploads/images/" # Image URL path

502

FAB_IMG_SIZE = (150, 150, True) # Image resize dimensions

503

504

# Rate limiting

505

RATELIMIT_ENABLED = False # Enable rate limiting

506

RATELIMIT_STORAGE_URL = "memory://" # Rate limit storage backend

507

RATELIMIT_HEADERS_ENABLED = True # Include rate limit headers

508

509

# Usage in configuration

510

class Config:

511

# Basic configuration

512

FAB_APP_NAME = "My Application"

513

FAB_APP_THEME = "cerulean.css"

514

FAB_APP_ICON = "/static/img/logo.png"

515

516

# Security

517

FAB_UPDATE_PERMS = True

518

FAB_PASSWORD_COMPLEXITY_ENABLED = True

519

FAB_AUTH_LIMIT_PER_USER = 5 # Max 5 login attempts

520

521

# API

522

FAB_API_MAX_PAGE_SIZE = 50

523

FAB_API_SHOW_STACKTRACE = False # Hide in production

524

525

# File uploads

526

FAB_UPLOAD_FOLDER = "/var/uploads/"

527

FAB_IMG_SIZE = (200, 200, True) # Larger thumbnails

528

529

# Rate limiting

530

RATELIMIT_ENABLED = True

531

RATELIMIT_STORAGE_URL = "redis://localhost:6379"

532

533

# Feature flags

534

class FeatureFlags:

535

"""Feature flags for conditional functionality."""

536

537

# UI features

538

ENABLE_CHARTS = True # Enable chart views

539

ENABLE_FILE_UPLOADS = True # Enable file upload fields

540

ENABLE_BULK_ACTIONS = True # Enable bulk actions on lists

541

ENABLE_EXPORT = True # Enable data export functionality

542

543

# Security features

544

ENABLE_PASSWORD_HISTORY = False # Track password history

545

ENABLE_SESSION_TIMEOUT = True # Enable session timeout

546

ENABLE_LOGIN_AUDIT = True # Log all login attempts

547

548

# API features

549

ENABLE_API_RATE_LIMITING = True # Enable API rate limiting

550

ENABLE_API_CACHING = False # Enable API response caching

551

ENABLE_OPENAPI_DOCS = True # Enable OpenAPI documentation

552

553

# Environment-specific constants

554

class ProductionConfig(Config):

555

FAB_API_SHOW_STACKTRACE = False

556

RATELIMIT_ENABLED = True

557

FAB_PASSWORD_COMPLEXITY_ENABLED = True

558

559

class DevelopmentConfig(Config):

560

FAB_API_SHOW_STACKTRACE = True

561

RATELIMIT_ENABLED = False

562

FAB_UPDATE_PERMS = True

563

564

class TestingConfig(Config):

565

TESTING = True

566

FAB_UPDATE_PERMS = False

567

WTF_CSRF_ENABLED = False

568

```