or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

extension-setup.mdform-rendering.mdindex.mdnavigation.mdpagination.mdpython-utilities.mdtable-rendering.mdutilities.md

python-utilities.mddocs/

0

# Python Utilities

1

2

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

3

4

## Capabilities

5

6

### Form Field Detection

7

8

Utility function for identifying hidden form fields in templates.

9

10

```python { .api }

11

def is_hidden_field_filter(field) -> bool:

12

"""

13

Check if a WTForms field is a hidden field.

14

15

Args:

16

field: WTForms field object

17

18

Returns:

19

bool: True if field is an instance of HiddenField, False otherwise

20

21

Usage:

22

- Used as Jinja filter: bootstrap_is_hidden_field

23

- Registered globally in Jinja environment

24

- Helps templates conditionally render hidden fields

25

26

Example:

27

{% for field in form %}

28

{% if not bootstrap_is_hidden_field(field) %}

29

{{ render_field(field) }}

30

{% endif %}

31

{% endfor %}

32

"""

33

```

34

35

### Table Title Generation

36

37

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

38

39

```python { .api }

40

def get_table_titles(data, primary_key, primary_key_title) -> list:

41

"""

42

Detect and build table titles from SQLAlchemy ORM objects.

43

44

Args:

45

data (list): List of SQLAlchemy model instances

46

primary_key (str): Primary key field name

47

primary_key_title (str): Display title for primary key column

48

49

Returns:

50

list: List of (field_name, display_title) tuples

51

52

Behavior:

53

- Extracts column names from first object's __table__.columns

54

- Converts snake_case to Title Case (user_name → User Name)

55

- Excludes fields starting with underscore (private fields)

56

- Sets primary key column to specified title

57

- Returns empty list if data is empty

58

59

Requirements:

60

- Only works with SQLAlchemy ORM objects

61

- Objects must have __table__ attribute with columns

62

63

Example:

64

User model columns: id, first_name, last_name, email, _password_hash

65

Returns: [('id', 'User ID'), ('first_name', 'First Name'),

66

('last_name', 'Last Name'), ('email', 'Email')]

67

"""

68

```

69

70

### Template Error Handling

71

72

Helper function for raising runtime errors from within Jinja templates.

73

74

```python { .api }

75

def raise_helper(message) -> None:

76

"""

77

Raise a RuntimeError from within templates.

78

79

Args:

80

message (str): Error message to display

81

82

Raises:

83

RuntimeError: Always raises with provided message

84

85

Usage:

86

- Registered as 'raise' in Jinja globals

87

- Allows templates to throw errors for debugging

88

- Useful for template development and error handling

89

90

Template Usage:

91

{% if not required_variable %}

92

{{ raise('Required variable is missing') }}

93

{% endif %}

94

"""

95

```

96

97

### Switch Field Component

98

99

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

100

101

```python { .api }

102

class SwitchField(BooleanField):

103

"""

104

A wrapper field for BooleanField that renders as a Bootstrap switch.

105

106

Inherits from WTForms BooleanField with identical functionality

107

but renders with Bootstrap switch styling instead of checkbox.

108

109

Attributes:

110

All BooleanField attributes and methods available

111

112

Bootstrap Versions:

113

- Bootstrap 4: Uses custom-switch classes

114

- Bootstrap 5: Uses form-switch classes

115

"""

116

117

def __init__(self, label=None, **kwargs):

118

"""

119

Initialize switch field.

120

121

Args:

122

label (str, optional): Field label text

123

**kwargs: Additional arguments passed to BooleanField

124

125

Supported kwargs:

126

- validators: List of validation functions

127

- default: Default value (True/False)

128

- description: Help text for field

129

- render_kw: Additional HTML attributes

130

"""

131

```

132

133

### Global Template Variables

134

135

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

136

137

```python { .api }

138

# Available in all templates after Bootstrap extension initialization

139

bootstrap_is_hidden_field # is_hidden_field_filter function

140

get_table_titles # Table title generation function

141

warn # warnings.warn function for deprecation notices

142

raise # raise_helper function for template errors

143

bootstrap # Extension instance with load_css(), load_js(), etc.

144

```

145

146

## Usage Examples

147

148

### Hidden Field Detection

149

150

```python

151

# Form definition

152

class ContactForm(FlaskForm):

153

name = StringField('Name', validators=[DataRequired()])

154

email = StringField('Email', validators=[DataRequired(), Email()])

155

csrf_token = HiddenField() # CSRF protection

156

honeypot = HiddenField() # Bot detection

157

message = TextAreaField('Message', validators=[DataRequired()])

158

submit = SubmitField('Send Message')

159

```

160

161

```html

162

<!-- Template - render only visible fields -->

163

<form method="post">

164

{% for field in form %}

165

{% if not bootstrap_is_hidden_field(field) and field.type != 'SubmitField' %}

166

<div class="mb-3">

167

{{ field.label(class="form-label") }}

168

{{ field(class="form-control") }}

169

{% if field.errors %}

170

{% for error in field.errors %}

171

<div class="text-danger">{{ error }}</div>

172

{% endfor %}

173

{% endif %}

174

</div>

175

{% endif %}

176

{% endfor %}

177

178

<!-- Render hidden fields separately -->

179

{% for field in form %}

180

{% if bootstrap_is_hidden_field(field) %}

181

{{ field() }}

182

{% endif %}

183

{% endfor %}

184

185

{{ form.submit(class="btn btn-primary") }}

186

</form>

187

```

188

189

### Automatic Table Titles

190

191

```python

192

# SQLAlchemy model

193

class Product(db.Model):

194

id = db.Column(db.Integer, primary_key=True)

195

product_name = db.Column(db.String(100), nullable=False)

196

unit_price = db.Column(db.Numeric(10, 2))

197

in_stock = db.Column(db.Boolean, default=True)

198

category_id = db.Column(db.Integer, db.ForeignKey('category.id'))

199

created_at = db.Column(db.DateTime, default=datetime.utcnow)

200

_internal_code = db.Column(db.String(50)) # Excluded (starts with _)

201

202

# View function

203

@app.route('/products')

204

def products():

205

products = Product.query.all()

206

207

# Automatically generate titles

208

titles = get_table_titles(products, 'id', 'Product ID')

209

# Returns: [('id', 'Product ID'), ('product_name', 'Product Name'),

210

# ('unit_price', 'Unit Price'), ('in_stock', 'In Stock'),

211

# ('category_id', 'Category Id'), ('created_at', 'Created At')]

212

213

return render_template('products.html', products=products, titles=titles)

214

```

215

216

```html

217

<!-- Template with auto-generated titles -->

218

{% from 'base/table.html' import render_table %}

219

220

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

221

```

222

223

### Custom Table Titles

224

225

```python

226

# Override auto-generated titles with custom ones

227

@app.route('/products')

228

def products():

229

products = Product.query.all()

230

231

# Custom titles for better display

232

titles = [

233

('id', '#'),

234

('product_name', 'Product'),

235

('unit_price', 'Price ($)'),

236

('in_stock', 'Available'),

237

('created_at', 'Date Added')

238

]

239

240

return render_template('products.html', products=products, titles=titles)

241

```

242

243

### Switch Field Usage

244

245

```python

246

# Form with switch fields

247

class UserPreferencesForm(FlaskForm):

248

email_notifications = SwitchField('Email Notifications')

249

sms_notifications = SwitchField('SMS Notifications')

250

dark_mode = SwitchField('Dark Mode')

251

auto_save = SwitchField('Auto-save Documents')

252

public_profile = SwitchField('Public Profile')

253

submit = SubmitField('Save Preferences')

254

```

255

256

```html

257

<!-- Template rendering switches -->

258

{% from 'bootstrap5/form.html' import render_field %}

259

260

<form method="post">

261

{{ form.hidden_tag() }}

262

263

<div class="mb-4">

264

<h5>Notification Settings</h5>

265

{{ render_field(form.email_notifications) }}

266

{{ render_field(form.sms_notifications) }}

267

</div>

268

269

<div class="mb-4">

270

<h5>Interface Settings</h5>

271

{{ render_field(form.dark_mode) }}

272

{{ render_field(form.auto_save) }}

273

</div>

274

275

<div class="mb-4">

276

<h5>Privacy Settings</h5>

277

{{ render_field(form.public_profile) }}

278

</div>

279

280

{{ render_field(form.submit) }}

281

</form>

282

```

283

284

### Template Error Handling

285

286

```html

287

<!-- Debug template with error checking -->

288

{% if not user %}

289

{{ raise('User object is required but not provided') }}

290

{% endif %}

291

292

{% if not user.permissions %}

293

{{ raise('User permissions not loaded') }}

294

{% endif %}

295

296

<!-- Development mode template validation -->

297

{% if config.DEBUG %}

298

{% if not posts %}

299

{{ raise('Posts data missing in template context') }}

300

{% endif %}

301

302

{% if posts|length == 0 %}

303

<!-- This won't raise an error, just show message -->

304

<p>No posts available</p>

305

{% endif %}

306

{% endif %}

307

```

308

309

### Complex Field Detection

310

311

```python

312

# Custom form field detection logic

313

class AdvancedForm(FlaskForm):

314

# Regular fields

315

title = StringField('Title')

316

content = TextAreaField('Content')

317

318

# Hidden fields

319

csrf_token = HiddenField()

320

user_id = HiddenField()

321

322

# Special fields

323

remember_me = BooleanField('Remember Me')

324

auto_publish = SwitchField('Auto-publish')

325

326

submit = SubmitField('Save')

327

```

328

329

```html

330

<!-- Template with advanced field handling -->

331

<form method="post">

332

<!-- Hidden fields first -->

333

{% for field in form if bootstrap_is_hidden_field(field) %}

334

{{ field() }}

335

{% endfor %}

336

337

<!-- Regular input fields -->

338

{% for field in form if not bootstrap_is_hidden_field(field) and field.type not in ['BooleanField', 'SwitchField', 'SubmitField'] %}

339

{{ render_field(field) }}

340

{% endfor %}

341

342

<!-- Boolean and switch fields in a group -->

343

<div class="mb-3">

344

<label class="form-label">Options</label>

345

{% for field in form if field.type in ['BooleanField', 'SwitchField'] %}

346

{{ render_field(field) }}

347

{% endfor %}

348

</div>

349

350

<!-- Submit button -->

351

{% for field in form if field.type == 'SubmitField' %}

352

{{ render_field(field) }}

353

{% endfor %}

354

</form>

355

```

356

357

### Utility Function Chaining

358

359

```python

360

# View combining multiple utilities

361

@app.route('/admin/users')

362

def admin_users():

363

users = User.query.all()

364

365

# Generate titles and customize

366

titles = get_table_titles(users, 'id', '#')

367

368

# Customize specific titles

369

title_dict = dict(titles)

370

title_dict['is_active'] = 'Status'

371

title_dict['last_login'] = 'Last Seen'

372

titles = list(title_dict.items())

373

374

return render_template('admin/users.html', users=users, titles=titles)

375

```

376

377

### Integration with Flask-Admin

378

379

```python

380

# Using utilities with Flask-Admin

381

from flask_admin.contrib.sqla import ModelView

382

383

class UserAdmin(ModelView):

384

def on_model_change(self, form, model, is_created):

385

# Use raise_helper for validation in admin

386

if not model.email:

387

raise_helper('Email is required for user accounts')

388

389

super().on_model_change(form, model, is_created)

390

391

def scaffold_list_columns(self):

392

# Use get_table_titles for admin list view

393

columns = super().scaffold_list_columns()

394

# Customize column display based on auto-generated titles

395

return columns

396

```

397

398

### Form Validation with Utilities

399

400

```python

401

# Custom validator using utilities

402

def validate_required_fields(form):

403

"""Validate that all non-hidden fields have values"""

404

errors = []

405

406

for field in form:

407

if not is_hidden_field_filter(field) and hasattr(field, 'data'):

408

if field.data is None or field.data == '':

409

errors.append(f'{field.label.text} is required')

410

411

return errors

412

413

# Usage in view

414

@app.route('/submit', methods=['POST'])

415

def submit_form():

416

form = MyForm()

417

418

if form.validate_on_submit():

419

# Additional validation

420

validation_errors = validate_required_fields(form)

421

if validation_errors:

422

for error in validation_errors:

423

flash(error, 'error')

424

return render_template('form.html', form=form)

425

426

# Process form

427

return redirect(url_for('success'))

428

429

return render_template('form.html', form=form)

430

```

431

432

## Best Practices

433

434

### Field Detection

435

- Use `bootstrap_is_hidden_field` consistently for hidden field detection

436

- Always render hidden fields separately from visible fields

437

- Include CSRF tokens and other security fields as hidden

438

439

### Table Titles

440

- Use `get_table_titles` for initial title generation

441

- Customize generated titles for better user experience

442

- Consider internationalization when overriding titles

443

444

### Switch Fields

445

- Use for boolean settings and preferences

446

- Prefer switches over checkboxes for on/off toggles

447

- Group related switches together in forms

448

449

### Error Handling

450

- Use `raise_helper` for development debugging only

451

- Remove debug raises before production deployment

452

- Consider logging instead of raising in production templates