Flask Framework
Learning Objectives
- By the end of this lesson, you will be able to:
- - Install and set up Flask
- - Create Flask applications
- - Define routes and views
- - Use templates
- - Handle requests and responses
- - Work with URL parameters
- - Use request methods
- - Apply Flask best practices
- - Debug Flask applications
- - Build basic web applications
Lesson 19.2: Flask Framework
Learning Objectives
By the end of this lesson, you will be able to:
- Install and set up Flask
- Create Flask applications
- Define routes and views
- Use templates
- Handle requests and responses
- Work with URL parameters
- Use request methods
- Apply Flask best practices
- Debug Flask applications
- Build basic web applications
Introduction to Flask
Flask is a lightweight Python web framework that makes it easy to build web applications. It's known for its simplicity and flexibility.
Why Flask?
- Simple: Easy to learn and use
- Lightweight: Minimal dependencies
- Flexible: No enforced structure
- Extensible: Many extensions available
- Pythonic: Follows Python conventions
What is Flask?
Flask is a micro web framework written in Python. It provides tools, libraries, and technologies to build web applications.
Flask Installation and Setup
Installing Flask
# Install Flask
pip install flask
# Or with requirements.txt
pip install -r requirements.txt
Basic Flask Application
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello, World!'
if __name__ == '__main__':
app.run(debug=True)
Running Flask Application
# Run the application
python app.py
# Or use Flask CLI
flask run
# With debug mode
flask run --debug
Project Structure
myapp/
├── app.py
├── templates/
│ └── index.html
├── static/
│ ├── css/
│ └── js/
└── requirements.txt
Routes and Views
Basic Route
A route maps a URL to a Python function (view):
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Home Page'
@app.route('/about')
def about():
return 'About Page'
URL Parameters
Capture URL parameters:
@app.route('/user/<username>')
def show_user(username):
return f'User: {username}'
@app.route('/post/<int:post_id>')
def show_post(post_id):
return f'Post ID: {post_id}'
URL Parameter Types
# String (default)
@app.route('/user/<name>')
def user(name):
return f'User: {name}'
# Integer
@app.route('/post/<int:id>')
def post(id):
return f'Post: {id}'
# Float
@app.route('/price/<float:amount>')
def price(amount):
return f'Price: {amount}'
# Path (includes slashes)
@app.route('/path/<path:subpath>')
def path(subpath):
return f'Path: {subpath}'
Multiple Routes
One view can handle multiple routes:
@app.route('/')
@app.route('/home')
@app.route('/index')
def home():
return 'Home Page'
HTTP Methods
Specify allowed HTTP methods:
from flask import Flask, request
app = Flask(__name__)
# GET only (default)
@app.route('/users', methods=['GET'])
def get_users():
return 'List of users'
# POST only
@app.route('/users', methods=['POST'])
def create_user():
return 'Create user'
# Multiple methods
@app.route('/users/<int:user_id>', methods=['GET', 'PUT', 'DELETE'])
def user_operations(user_id):
if request.method == 'GET':
return f'Get user {user_id}'
elif request.method == 'PUT':
return f'Update user {user_id}'
elif request.method == 'DELETE':
return f'Delete user {user_id}'
Request Object
Access request data:
from flask import Flask, request
app = Flask(__name__)
@app.route('/example', methods=['GET', 'POST'])
def example():
# Request method
method = request.method
# Query parameters
name = request.args.get('name')
# Form data
email = request.form.get('email')
# JSON data
if request.is_json:
data = request.get_json()
# Headers
user_agent = request.headers.get('User-Agent')
return f'Method: {method}, Name: {name}'
Templates
What are Templates?
Templates are HTML files with dynamic content using Jinja2 templating engine.
Basic Template
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
<!-- templates/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
</head>
<body>
<h1>Welcome!</h1>
</body>
</html>
Template Variables
Pass variables to templates:
@app.route('/user/<name>')
def user(name):
return render_template('user.html', name=name)
<!-- templates/user.html -->
<h1>Hello, {{ name }}!</h1>
Template Filters
Jinja2 provides filters:
<!-- Uppercase -->
{{ name|upper }}
<!-- Lowercase -->
{{ name|lower }}
<!-- Title case -->
{{ name|title }}
<!-- Length -->
{{ items|length }}
<!-- Default value -->
{{ value|default('N/A') }}
Template Control Structures
If Statements
{% if user %}
<p>Welcome, {{ user.name }}!</p>
{% else %}
<p>Please log in.</p>
{% endif %}
For Loops
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
Loop Variables
{% for item in items %}
<p>Item {{ loop.index }}: {{ item }}</p>
{% if loop.first %}
<p>First item</p>
{% endif %}
{% if loop.last %}
<p>Last item</p>
{% endif %}
{% endfor %}
Template Inheritance
Base template:
<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}My App{% endblock %}</title>
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
<main>
{% block content %}{% endblock %}
</main>
<footer>
{% block footer %}{% endblock %}
</footer>
</body>
</html>
Child template:
<!-- templates/index.html -->
{% extends "base.html" %}
{% block title %}Home - My App{% endblock %}
{% block content %}
<h1>Welcome!</h1>
<p>This is the home page.</p>
{% endblock %}
Including Templates
<!-- templates/header.html -->
<header>
<h1>My Website</h1>
</header>
<!-- templates/index.html -->
{% include 'header.html' %}
<main>Content here</main>
Template Context
Pass multiple variables:
@app.route('/dashboard')
def dashboard():
user = {'name': 'Alice', 'email': 'alice@example.com'}
posts = ['Post 1', 'Post 2', 'Post 3']
return render_template('dashboard.html', user=user, posts=posts)
<!-- templates/dashboard.html -->
<h1>Dashboard</h1>
<p>User: {{ user.name }}</p>
<p>Email: {{ user.email }}</p>
<h2>Posts</h2>
<ul>
{% for post in posts %}
<li>{{ post }}</li>
{% endfor %}
</ul>
Static Files
Serving Static Files
Flask automatically serves files from the static folder:
myapp/
├── app.py
└── static/
├── css/
│ └── style.css
├── js/
│ └── script.js
└── images/
└── logo.png
Using Static Files in Templates
<!-- Link to CSS -->
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
<!-- Link to JavaScript -->
<script src="{{ url_for('static', filename='js/script.js') }}"></script>
<!-- Image -->
<img src="{{ url_for('static', filename='images/logo.png') }}" alt="Logo">
URL Building
url_for() Function
Generate URLs for routes:
from flask import Flask, url_for
app = Flask(__name__)
@app.route('/')
def index():
return 'Home'
@app.route('/user/<username>')
def user(username):
return f'User: {username}'
# Generate URLs
print(url_for('index')) # /
print(url_for('user', username='alice')) # /user/alice
Using url_for() in Templates
<a href="{{ url_for('index') }}">Home</a>
<a href="{{ url_for('user', username='alice') }}">Alice's Profile</a>
Request Handling
Accessing Request Data
from flask import Flask, request
app = Flask(__name__)
@app.route('/example', methods=['GET', 'POST'])
def example():
# Method
method = request.method
# Query parameters
page = request.args.get('page', 1, type=int)
# Form data
name = request.form.get('name')
# JSON
if request.is_json:
data = request.get_json()
# Files
file = request.files.get('file')
# Headers
content_type = request.headers.get('Content-Type')
return f'Method: {method}'
Response Objects
Create custom responses:
from flask import Flask, make_response, jsonify
app = Flask(__name__)
@app.route('/json')
def json_response():
return jsonify({'message': 'Hello', 'status': 'success'})
@app.route('/custom')
def custom_response():
response = make_response('Custom Response')
response.headers['X-Custom-Header'] = 'Value'
response.status_code = 201
return response
Redirects
from flask import Flask, redirect, url_for
app = Flask(__name__)
@app.route('/old')
def old():
return redirect(url_for('new'))
@app.route('/new')
def new():
return 'New page'
Practical Examples
Example 1: Simple Blog
from flask import Flask, render_template
app = Flask(__name__)
posts = [
{'id': 1, 'title': 'First Post', 'content': 'Content of first post'},
{'id': 2, 'title': 'Second Post', 'content': 'Content of second post'},
]
@app.route('/')
def index():
return render_template('index.html', posts=posts)
@app.route('/post/<int:post_id>')
def post(post_id):
post = next((p for p in posts if p['id'] == post_id), None)
if post:
return render_template('post.html', post=post)
return 'Post not found', 404
Example 2: User API
from flask import Flask, jsonify, request
app = Flask(__name__)
users = [
{'id': 1, 'name': 'Alice', 'email': 'alice@example.com'},
{'id': 2, 'name': 'Bob', 'email': 'bob@example.com'},
]
@app.route('/api/users', methods=['GET'])
def get_users():
return jsonify(users)
@app.route('/api/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
user = next((u for u in users if u['id'] == user_id), None)
if user:
return jsonify(user)
return jsonify({'error': 'User not found'}), 404
@app.route('/api/users', methods=['POST'])
def create_user():
data = request.get_json()
new_user = {
'id': len(users) + 1,
'name': data.get('name'),
'email': data.get('email')
}
users.append(new_user)
return jsonify(new_user), 201
Example 3: Form Handling
from flask import Flask, render_template, request, redirect, url_for
app = Flask(__name__)
@app.route('/contact', methods=['GET', 'POST'])
def contact():
if request.method == 'POST':
name = request.form.get('name')
email = request.form.get('email')
message = request.form.get('message')
# Process form data
return redirect(url_for('thank_you'))
return render_template('contact.html')
@app.route('/thank-you')
def thank_you():
return render_template('thank_you.html')
Common Mistakes and Pitfalls
1. Not Using url_for()
# WRONG: Hard-coded URLs
<a href="/user/alice">Profile</a>
# CORRECT: Use url_for()
<a href="{{ url_for('user', username='alice') }}">Profile</a>
2. Not Handling Errors
# WRONG: No error handling
user = users[user_id] # May raise IndexError
# CORRECT: Handle errors
user = next((u for u in users if u['id'] == user_id), None)
if not user:
return 'User not found', 404
3. Not Escaping Template Variables
# Jinja2 auto-escapes by default, but be careful
# Use |safe filter only when you trust the content
{{ user_input|safe }} # Only if trusted!
4. Running in Production with Debug
# WRONG: Debug mode in production
app.run(debug=True) # Security risk!
# CORRECT: Debug only in development
if __name__ == '__main__':
app.run(debug=True) # Only for development
Best Practices
1. Use Application Factory Pattern
from flask import Flask
def create_app():
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello'
return app
if __name__ == '__main__':
app = create_app()
app.run(debug=True)
2. Organize Routes
# app.py
from flask import Flask
app = Flask(__name__)
# Import routes
from routes import main, users, posts
app.register_blueprint(main.bp)
app.register_blueprint(users.bp)
app.register_blueprint(posts.bp)
3. Use Configuration
from flask import Flask
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
app.config['DEBUG'] = True
4. Error Handling
@app.errorhandler(404)
def not_found(error):
return render_template('404.html'), 404
@app.errorhandler(500)
def internal_error(error):
return render_template('500.html'), 500
5. Use Blueprints
from flask import Blueprint
bp = Blueprint('users', __name__, url_prefix='/users')
@bp.route('/')
def index():
return 'Users'
Practice Exercise
Exercise: Flask App
Objective: Create a Flask web application.
Instructions:
-
Create a Flask application with the following structure:
flask_app/ ├── app.py ├── templates/ │ ├── base.html │ ├── index.html │ └── user.html └── static/ └── css/ └── style.css -
Create a web application that:
- Has multiple routes
- Uses templates
- Handles URL parameters
- Serves static files
- Demonstrates Flask features
-
Your application should include:
- Home page
- User profile page
- About page
- Template inheritance
- Static CSS
- URL building
Example Solution:
"""
Flask Application Practice
This program demonstrates Flask framework basics.
"""
from flask import Flask, render_template, url_for, request, redirect
app = Flask(__name__)
# Sample data
users = [
{'id': 1, 'name': 'Alice', 'email': 'alice@example.com', 'bio': 'Software Developer'},
{'id': 2, 'name': 'Bob', 'email': 'bob@example.com', 'bio': 'Data Scientist'},
{'id': 3, 'name': 'Charlie', 'email': 'charlie@example.com', 'bio': 'Web Designer'},
]
posts = [
{'id': 1, 'title': 'First Post', 'content': 'This is the first post', 'author': 'Alice'},
{'id': 2, 'title': 'Second Post', 'content': 'This is the second post', 'author': 'Bob'},
]
# Routes
@app.route('/')
def index():
return render_template('index.html', users=users, posts=posts)
@app.route('/about')
def about():
return render_template('about.html')
@app.route('/user/<int:user_id>')
def user(user_id):
user = next((u for u in users if u['id'] == user_id), None)
if user:
user_posts = [p for p in posts if p['author'] == user['name']]
return render_template('user.html', user=user, posts=user_posts)
return render_template('404.html'), 404
@app.route('/post/<int:post_id>')
def post(post_id):
post = next((p for p in posts if p['id'] == post_id), None)
if post:
return render_template('post.html', post=post)
return render_template('404.html'), 404
@app.route('/search')
def search():
query = request.args.get('q', '')
if query:
filtered_posts = [p for p in posts if query.lower() in p['title'].lower()]
return render_template('search.html', query=query, posts=filtered_posts)
return render_template('search.html', query='', posts=[])
# Error handlers
@app.errorhandler(404)
def not_found(error):
return render_template('404.html'), 404
if __name__ == '__main__':
app.run(debug=True)
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}My Flask App{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
<nav>
<a href="{{ url_for('index') }}">Home</a>
<a href="{{ url_for('about') }}">About</a>
</nav>
<main>
{% block content %}{% endblock %}
</main>
<footer>
<p>© 2024 My Flask App</p>
</footer>
</body>
</html>
<!-- templates/index.html -->
{% extends "base.html" %}
{% block title %}Home - My Flask App{% endblock %}
{% block content %}
<h1>Welcome to My Flask App</h1>
<h2>Users</h2>
<ul>
{% for user in users %}
<li><a href="{{ url_for('user', user_id=user.id) }}">{{ user.name }}</a></li>
{% endfor %}
</ul>
<h2>Recent Posts</h2>
<ul>
{% for post in posts %}
<li><a href="{{ url_for('post', post_id=post.id) }}">{{ post.title }}</a></li>
{% endfor %}
</ul>
{% endblock %}
<!-- templates/user.html -->
{% extends "base.html" %}
{% block title %}{{ user.name }} - My Flask App{% endblock %}
{% block content %}
<h1>{{ user.name }}</h1>
<p><strong>Email:</strong> {{ user.email }}</p>
<p><strong>Bio:</strong> {{ user.bio }}</p>
<h2>Posts by {{ user.name }}</h2>
{% if posts %}
<ul>
{% for post in posts %}
<li><a href="{{ url_for('post', post_id=post.id) }}">{{ post.title }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No posts yet.</p>
{% endif %}
{% endblock %}
<!-- templates/about.html -->
{% extends "base.html" %}
{% block title %}About - My Flask App{% endblock %}
{% block content %}
<h1>About</h1>
<p>This is a Flask web application demonstrating basic features.</p>
{% endblock %}
<!-- templates/404.html -->
{% extends "base.html" %}
{% block title %}404 - Not Found{% endblock %}
{% block content %}
<h1>404 - Page Not Found</h1>
<p>The page you're looking for doesn't exist.</p>
<a href="{{ url_for('index') }}">Go Home</a>
{% endblock %}
/* static/css/style.css */
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
nav {
background-color: #333;
padding: 10px;
margin-bottom: 20px;
}
nav a {
color: white;
text-decoration: none;
margin-right: 20px;
}
nav a:hover {
text-decoration: underline;
}
footer {
margin-top: 40px;
padding-top: 20px;
border-top: 1px solid #ccc;
text-align: center;
color: #666;
}
Expected Output: A working Flask web application with multiple pages, navigation, and styling.
Challenge (Optional):
- Add a contact form
- Implement user authentication
- Add a blog post creation page
- Create an API endpoint
- Add pagination for posts
Key Takeaways
- Flask installation -
pip install flask - Basic app structure - Flask instance, routes, run
- Routes - map URLs to functions
- URL parameters - capture dynamic values
- HTTP methods - GET, POST, PUT, DELETE
- Templates - Jinja2 templating engine
- Template inheritance - base templates
- Static files - CSS, JS, images
- url_for() - generate URLs
- Request handling - access request data
- Response objects - custom responses
- Error handling - error handlers
- Best practices - application factory, blueprints, configuration
- Debug mode - only for development
- Project structure - organize files properly
Quiz: Flask Basics
Test your understanding with these questions:
-
What is Flask?
- A) A database
- B) A web framework
- C) A programming language
- D) A database framework
-
How do you create a Flask app?
- A) Flask()
- B) Flask(name)
- C) create_app()
- D) new Flask()
-
What decorator defines a route?
- A) @route
- B) @app.route
- C) @flask.route
- D) @path
-
What template engine does Flask use?
- A) Django templates
- B) Jinja2
- C) Mustache
- D) Handlebars
-
Where are templates stored?
- A) templates/ folder
- B) views/ folder
- C) html/ folder
- D) pages/ folder
-
What function renders templates?
- A) render()
- B) render_template()
- C) template()
- D) show_template()
-
What function generates URLs?
- A) url()
- B) url_for()
- C) generate_url()
- D) make_url()
-
What is the default HTTP method?
- A) POST
- B) GET
- C) PUT
- D) DELETE
-
How do you access query parameters?
- A) request.query
- B) request.args
- C) request.params
- D) request.get
-
What should you NOT do in production?
- A) Use templates
- B) Use routes
- C) Use debug=True
- D) Use Flask
Answers:
- B) A web framework (Flask definition)
- B) Flask(name) (creating Flask app)
- B) @app.route (route decorator)
- B) Jinja2 (Flask template engine)
- A) templates/ folder (template location)
- B) render_template() (template rendering)
- B) url_for() (URL generation)
- B) GET (default HTTP method)
- B) request.args (query parameters)
- C) Use debug=True (production security)
Next Steps
Excellent work! You've mastered Flask basics. You now understand:
- Flask installation and setup
- Routes and views
- Templates
- How to build web applications
What's Next?
- Lesson 19.3: Flask Advanced
- Learn forms and validation
- Understand database integration
- Explore advanced Flask features
Additional Resources
- Flask Documentation: flask.palletsprojects.com/
- Jinja2 Templates: jinja.palletsprojects.com/
- Flask Tutorial: flask.palletsprojects.com/en/latest/tutorial/
Lesson completed! You're ready to move on to the next lesson.