0
# Helpers and Utilities
1
2
Flask provides utility functions for common web development tasks including redirects, error handling, file operations, and message flashing.
3
4
## Capabilities
5
6
### HTTP Response Helpers
7
8
Functions for creating HTTP responses and handling redirects.
9
10
```python { .api }
11
def redirect(location: str, code: int = 302, Response: type | None = None) -> Response:
12
"""
13
Create a redirect response to a different location.
14
15
Args:
16
location: URL to redirect to
17
code: HTTP status code (301, 302, 303, 307, 308)
18
Response: Response class to use
19
20
Returns:
21
Redirect response object
22
"""
23
24
def abort(code: int | BaseResponse, *args, **kwargs) -> NoReturn:
25
"""
26
Abort the current request with an HTTP error.
27
28
Args:
29
code: HTTP status code or Response object
30
*args: Additional arguments for the exception
31
**kwargs: Additional keyword arguments
32
33
Raises:
34
HTTPException: With the specified status code
35
"""
36
37
def make_response(*args) -> Response:
38
"""
39
Convert arguments into a Response object.
40
41
Args:
42
*args: Arguments that would normally be returned from a view function
43
44
Returns:
45
Response object that can be modified before returning
46
"""
47
```
48
49
### File Operations
50
51
Functions for sending files and handling file downloads.
52
53
```python { .api }
54
def send_file(
55
path_or_file: os.PathLike | str | IO[bytes],
56
mimetype: str | None = None,
57
as_attachment: bool = False,
58
download_name: str | None = None,
59
conditional: bool = True,
60
etag: bool | str = True,
61
last_modified: datetime | int | float | None = None,
62
max_age: int | Callable[[str | None], int | None] | None = None
63
) -> Response:
64
"""
65
Send a file to the client.
66
67
Args:
68
path_or_file: Path to file or file-like object
69
mimetype: MIME type (auto-detected if None)
70
as_attachment: Send as attachment (triggers download)
71
download_name: Filename for download
72
conditional: Enable conditional responses (304 Not Modified)
73
etag: Enable ETag header (True, False, or custom ETag)
74
last_modified: Last modified timestamp
75
max_age: Cache max age in seconds
76
77
Returns:
78
Response with file contents
79
"""
80
81
def send_from_directory(
82
directory: str,
83
path: str,
84
**kwargs
85
) -> Response:
86
"""
87
Send a file from a directory safely.
88
89
Args:
90
directory: Directory containing the file
91
path: Relative path to file within directory
92
**kwargs: Additional arguments passed to send_file()
93
94
Returns:
95
Response with file contents
96
97
Raises:
98
NotFound: If file doesn't exist or path is unsafe
99
"""
100
```
101
102
### Message Flashing
103
104
Functions for displaying one-time messages to users.
105
106
```python { .api }
107
def flash(message: str, category: str = "message") -> None:
108
"""
109
Flash a message that will be displayed on the next request.
110
111
Args:
112
message: Message text to flash
113
category: Message category (e.g., 'error', 'info', 'warning')
114
"""
115
116
def get_flashed_messages(
117
with_categories: bool = False,
118
category_filter: list[str] = []
119
) -> list[str] | list[tuple[str, str]]:
120
"""
121
Get and remove all flashed messages.
122
123
Args:
124
with_categories: Return (category, message) tuples
125
category_filter: Only return messages from these categories
126
127
Returns:
128
List of messages or (category, message) tuples
129
"""
130
```
131
132
### Template Utilities
133
134
Functions for working with templates outside of normal rendering.
135
136
```python { .api }
137
def get_template_attribute(template_name: str, attribute: str) -> Any:
138
"""
139
Load a template and get one of its attributes.
140
141
Args:
142
template_name: Template filename
143
attribute: Attribute name to retrieve
144
145
Returns:
146
Template attribute value
147
148
Raises:
149
TemplateNotFound: If template doesn't exist
150
AttributeError: If attribute doesn't exist
151
"""
152
```
153
154
### Context Streaming
155
156
Function for maintaining request context in generators and iterators.
157
158
```python { .api }
159
def stream_with_context(generator_or_function) -> Iterator:
160
"""
161
Stream with the current request context.
162
163
Args:
164
generator_or_function: Generator function or iterator
165
166
Returns:
167
Iterator that preserves request context
168
"""
169
```
170
171
## Usage Examples
172
173
### Redirects and URL Handling
174
175
```python
176
from flask import Flask, redirect, url_for, request
177
178
app = Flask(__name__)
179
180
@app.route('/')
181
def home():
182
return 'Home Page'
183
184
@app.route('/login')
185
def login():
186
return 'Login Page'
187
188
@app.route('/dashboard')
189
def dashboard():
190
# Redirect to login if not authenticated
191
if not session.get('logged_in'):
192
return redirect(url_for('login'))
193
return 'Dashboard'
194
195
@app.route('/old-page')
196
def old_page():
197
# Permanent redirect (301)
198
return redirect(url_for('home'), code=301)
199
200
@app.route('/external')
201
def external_redirect():
202
# Redirect to external URL
203
return redirect('https://www.example.com')
204
205
@app.route('/conditional-redirect')
206
def conditional_redirect():
207
next_page = request.args.get('next')
208
if next_page:
209
return redirect(next_page)
210
return redirect(url_for('home'))
211
```
212
213
### Error Handling with Abort
214
215
```python
216
from flask import Flask, abort, request, jsonify
217
218
app = Flask(__name__)
219
220
@app.route('/user/<int:user_id>')
221
def get_user(user_id):
222
# Validate user ID
223
if user_id <= 0:
224
abort(400, description="Invalid user ID")
225
226
# Simulate user lookup
227
if user_id > 1000:
228
abort(404, description="User not found")
229
230
# Simulate permission check
231
if user_id == 999:
232
abort(403, description="Access denied")
233
234
return {'id': user_id, 'name': f'User {user_id}'}
235
236
@app.route('/admin')
237
def admin_area():
238
# Check authentication
239
if not session.get('logged_in'):
240
abort(401) # Unauthorized
241
242
# Check admin privileges
243
if not session.get('is_admin'):
244
abort(403) # Forbidden
245
246
return 'Admin Area'
247
248
@app.route('/api/data', methods=['POST'])
249
def create_data():
250
if not request.is_json:
251
abort(400, description="Content-Type must be application/json")
252
253
data = request.get_json()
254
if not data or 'name' not in data:
255
abort(422, description="Missing required field: name")
256
257
return jsonify({'status': 'created', 'data': data}), 201
258
```
259
260
### Response Creation with make_response
261
262
```python
263
from flask import Flask, make_response, render_template, jsonify
264
265
app = Flask(__name__)
266
267
@app.route('/custom-headers')
268
def custom_headers():
269
# Create response with custom headers
270
response = make_response(render_template('index.html'))
271
response.headers['X-Custom-Header'] = 'Custom Value'
272
response.headers['Cache-Control'] = 'no-cache'
273
return response
274
275
@app.route('/api/data')
276
def api_data():
277
# Create JSON response with custom status and headers
278
data = {'message': 'Hello, World!'}
279
response = make_response(jsonify(data))
280
response.status_code = 200
281
response.headers['X-API-Version'] = '1.0'
282
return response
283
284
@app.route('/download-csv')
285
def download_csv():
286
csv_data = "name,email\nJohn,john@example.com\nJane,jane@example.com"
287
response = make_response(csv_data)
288
response.headers['Content-Type'] = 'text/csv'
289
response.headers['Content-Disposition'] = 'attachment; filename=users.csv'
290
return response
291
```
292
293
### File Downloads and Uploads
294
295
```python
296
from flask import Flask, send_file, send_from_directory, request, abort
297
import os
298
from werkzeug.utils import secure_filename
299
300
app = Flask(__name__)
301
app.config['UPLOAD_FOLDER'] = 'uploads'
302
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max
303
304
@app.route('/download/<filename>')
305
def download_file(filename):
306
# Send file from uploads directory
307
try:
308
return send_from_directory(
309
app.config['UPLOAD_FOLDER'],
310
filename,
311
as_attachment=True
312
)
313
except FileNotFoundError:
314
abort(404)
315
316
@app.route('/view/<filename>')
317
def view_file(filename):
318
# View file in browser (not as download)
319
return send_from_directory(
320
app.config['UPLOAD_FOLDER'],
321
filename,
322
as_attachment=False
323
)
324
325
@app.route('/generate-report')
326
def generate_report():
327
# Generate and send a file
328
import tempfile
329
import csv
330
331
# Create temporary file
332
temp_file = tempfile.NamedTemporaryFile(
333
mode='w',
334
suffix='.csv',
335
delete=False
336
)
337
338
# Write CSV data
339
writer = csv.writer(temp_file)
340
writer.writerow(['Name', 'Email', 'Role'])
341
writer.writerow(['John Doe', 'john@example.com', 'Admin'])
342
writer.writerow(['Jane Smith', 'jane@example.com', 'User'])
343
temp_file.close()
344
345
return send_file(
346
temp_file.name,
347
as_attachment=True,
348
download_name='users.csv',
349
mimetype='text/csv'
350
)
351
352
@app.route('/upload', methods=['POST'])
353
def upload_file():
354
if 'file' not in request.files:
355
abort(400, description="No file uploaded")
356
357
file = request.files['file']
358
if file.filename == '':
359
abort(400, description="No file selected")
360
361
if file:
362
filename = secure_filename(file.filename)
363
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
364
file.save(filepath)
365
366
return {
367
'status': 'uploaded',
368
'filename': filename,
369
'download_url': url_for('download_file', filename=filename)
370
}
371
```
372
373
### Message Flashing
374
375
```python
376
from flask import Flask, flash, get_flashed_messages, render_template, redirect, url_for, request
377
378
app = Flask(__name__)
379
app.secret_key = 'your-secret-key'
380
381
@app.route('/')
382
def home():
383
return render_template('home.html')
384
385
@app.route('/login', methods=['GET', 'POST'])
386
def login():
387
if request.method == 'POST':
388
username = request.form.get('username')
389
password = request.form.get('password')
390
391
if not username or not password:
392
flash('Username and password are required', 'error')
393
elif username == 'admin' and password == 'secret':
394
flash('Login successful!', 'success')
395
session['logged_in'] = True
396
return redirect(url_for('dashboard'))
397
else:
398
flash('Invalid credentials', 'error')
399
400
return render_template('login.html')
401
402
@app.route('/dashboard')
403
def dashboard():
404
if not session.get('logged_in'):
405
flash('Please log in to access the dashboard', 'warning')
406
return redirect(url_for('login'))
407
408
flash('Welcome to your dashboard!', 'info')
409
return render_template('dashboard.html')
410
411
@app.route('/profile', methods=['POST'])
412
def update_profile():
413
# Simulate profile update
414
name = request.form.get('name')
415
email = request.form.get('email')
416
417
if name and email:
418
flash('Profile updated successfully!', 'success')
419
else:
420
flash('Please fill in all fields', 'error')
421
422
return redirect(url_for('dashboard'))
423
424
@app.route('/messages')
425
def show_messages():
426
# Get messages with categories
427
messages = get_flashed_messages(with_categories=True)
428
return render_template('messages.html', messages=messages)
429
430
@app.route('/warnings')
431
def show_warnings():
432
# Get only warning messages
433
warnings = get_flashed_messages(category_filter=['warning', 'error'])
434
return render_template('warnings.html', warnings=warnings)
435
```
436
437
Template for displaying messages (`templates/base.html`):
438
```html
439
<!DOCTYPE html>
440
<html>
441
<head>
442
<title>Flask App</title>
443
<style>
444
.flash-message { padding: 10px; margin: 10px 0; border-radius: 4px; }
445
.flash-success { background-color: #d4edda; color: #155724; }
446
.flash-error { background-color: #f8d7da; color: #721c24; }
447
.flash-warning { background-color: #fff3cd; color: #856404; }
448
.flash-info { background-color: #d1ecf1; color: #0c5460; }
449
</style>
450
</head>
451
<body>
452
<!-- Display flashed messages -->
453
{% with messages = get_flashed_messages(with_categories=true) %}
454
{% if messages %}
455
{% for category, message in messages %}
456
<div class="flash-message flash-{{ category }}">
457
{{ message }}
458
</div>
459
{% endfor %}
460
{% endif %}
461
{% endwith %}
462
463
{% block content %}{% endblock %}
464
</body>
465
</html>
466
```
467
468
### Template Attributes
469
470
```python
471
from flask import Flask, get_template_attribute, render_template
472
473
app = Flask(__name__)
474
475
@app.route('/macro-example')
476
def macro_example():
477
# Get a macro from a template
478
render_form = get_template_attribute('macros.html', 'render_form')
479
480
# Use the macro programmatically
481
form_html = render_form(
482
action='/submit',
483
method='POST',
484
fields=[
485
{'name': 'username', 'type': 'text', 'label': 'Username'},
486
{'name': 'email', 'type': 'email', 'label': 'Email'}
487
]
488
)
489
490
return f'<h1>Generated Form</h1>{form_html}'
491
492
@app.route('/template-vars')
493
def template_vars():
494
# Get variables from template
495
config_data = get_template_attribute('config.html', 'app_config')
496
return {'config': config_data}
497
```
498
499
### Streaming with Context
500
501
```python
502
from flask import Flask, stream_with_context, request, g
503
import time
504
505
app = Flask(__name__)
506
507
@app.before_request
508
def before_request():
509
g.user_id = request.headers.get('X-User-ID', 'anonymous')
510
511
@app.route('/stream')
512
def stream_data():
513
def generate():
514
for i in range(10):
515
# Access request context variables
516
yield f"data: User {g.user_id}, Item {i}\n\n"
517
time.sleep(1)
518
519
return Response(
520
stream_with_context(generate()),
521
mimetype='text/plain'
522
)
523
524
@app.route('/sse')
525
def server_sent_events():
526
def event_stream():
527
count = 0
528
while count < 50:
529
# Use request context in generator
530
user_id = g.user_id
531
yield f"data: {{\"user\": \"{user_id}\", \"count\": {count}}}\n\n"
532
count += 1
533
time.sleep(2)
534
535
return Response(
536
stream_with_context(event_stream()),
537
mimetype='text/event-stream',
538
headers={
539
'Cache-Control': 'no-cache',
540
'Connection': 'keep-alive'
541
}
542
)
543
```
544
545
### Advanced File Handling
546
547
```python
548
from flask import Flask, send_file, request, abort
549
from datetime import datetime, timedelta
550
import mimetypes
551
import os
552
553
app = Flask(__name__)
554
555
@app.route('/file/<path:filename>')
556
def serve_file(filename):
557
file_path = os.path.join('files', filename)
558
559
if not os.path.exists(file_path):
560
abort(404)
561
562
# Get file info
563
stat = os.stat(file_path)
564
last_modified = datetime.fromtimestamp(stat.st_mtime)
565
566
return send_file(
567
file_path,
568
conditional=True,
569
last_modified=last_modified,
570
max_age=timedelta(hours=1).total_seconds()
571
)
572
573
@app.route('/image/<filename>')
574
def serve_image(filename):
575
image_path = os.path.join('images', filename)
576
577
if not os.path.exists(image_path):
578
abort(404)
579
580
# Auto-detect MIME type
581
mimetype, _ = mimetypes.guess_type(filename)
582
583
return send_file(
584
image_path,
585
mimetype=mimetype,
586
conditional=True,
587
max_age=timedelta(days=30).total_seconds()
588
)
589
590
@app.route('/cached-file/<filename>')
591
def cached_file(filename):
592
file_path = os.path.join('cache', filename)
593
594
# Custom max_age function
595
def get_max_age(filename):
596
if filename.endswith('.css') or filename.endswith('.js'):
597
return 86400 # 1 day
598
elif filename.endswith(('.jpg', '.png', '.gif')):
599
return 604800 # 1 week
600
return 3600 # 1 hour default
601
602
return send_file(
603
file_path,
604
max_age=get_max_age,
605
etag=True
606
)
607
```