Introduction to Flask
Flask is a micro web framework for Python that provides the essentials for building web applications without imposing rigid patterns. Its simplicity makes it perfect for beginners, while its extensibility satisfies the needs of complex applications.
What is Flask?
Flask is a lightweight WSGI (Web Server Gateway Interface) web application framework. It is designed to make getting started quick and easy, with the ability to scale up to complex applications. Flask provides the core tools - routing, request handling, and templating - while letting you choose additional components as needed.
Why it matters: Flask's "micro" philosophy means less boilerplate code, faster development cycles, and easier debugging. You add only what you need, keeping your application lean and maintainable.
Request-Response Cycle
The diagram above shows how Flask handles web requests: the client sends a request, Flask matches it to a route, executes the view function, and returns a response.
Why Choose Flask?
Lightweight
Minimal dependencies and small footprint. Start with just a few lines of code.
Flexible
No forced project structure. Choose your own database, template engine, and tools.
Well Documented
Excellent documentation and large community. Easy to find help and resources.
Installing Flask
Flask can be installed using pip. It is recommended to use a virtual environment to keep your project dependencies isolated.
# Create a virtual environment (recommended)
# python -m venv venv
# source venv/bin/activate (Linux/Mac)
# venv\Scripts\activate (Windows)
# Install Flask
# pip install flask
# Verify installation
import flask
print(f"Flask version: {flask.__version__}")
Always use a virtual environment for Flask projects. This keeps dependencies isolated and makes deployment easier.
Your First Flask App
Let's create a minimal Flask application to understand the basic structure. With just five lines of code, you can have a working web server.
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello, World!'
if __name__ == '__main__':
app.run(debug=True)
This minimal application creates a Flask instance, defines a single route, and runs the development server. Save this as app.py and run it with python app.py.
Understanding the Code
| Line | Purpose | Explanation |
|---|---|---|
Flask(__name__) |
Create app instance | Creates the Flask application object. __name__ helps Flask locate resources. |
@app.route('/') |
Route decorator | Maps the URL path "/" to the function below it. |
def hello() |
View function | Returns the response when this route is accessed. |
app.run(debug=True) |
Start server | Runs the development server with auto-reload enabled. |
Route Decorator Anatomy
@app.route('/users/')
def show_user(user_id):
return f'User ID: {user_id}'
debug=True during development. It enables auto-reload on code changes and provides helpful error pages. Never use debug mode in production!
Routing Fundamentals
Routes define the URLs your application responds to. The @app.route() decorator connects URL patterns to Python functions, making URL management intuitive and declarative.
Basic Routes
Each route decorator maps a URL pattern to a view function. When a user visits that URL, Flask calls the associated function and returns its output as the HTTP response.
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return 'Welcome to the Home Page!'
@app.route('/about')
def about():
return 'About Us Page'
@app.route('/contact')
def contact():
return 'Contact Us Page'
Each @app.route() decorator creates a unique endpoint. The function name becomes the endpoint name, used for URL generation with url_for().
Multiple Routes for One Function
A single view function can handle multiple URL patterns by stacking decorators.
@app.route('/')
@app.route('/home')
@app.route('/index')
def homepage():
return 'This is the homepage!'
# All three URLs point to the same function:
# / , /home , /index
Stacking multiple route decorators is useful for supporting legacy URLs or providing user-friendly aliases.
Trailing Slashes
Flask has specific behavior for trailing slashes. Understanding this prevents 404 errors.
# With trailing slash - works with or without slash
@app.route('/projects/')
def projects():
return 'Projects Page'
# /projects and /projects/ both work (redirects to /projects/)
# Without trailing slash - strict matching
@app.route('/about')
def about():
return 'About Page'
# /about works, but /about/ returns 404
Dynamic Routes
Dynamic routes allow you to capture variable parts of the URL and pass them as arguments to your view function. This is essential for building RESTful APIs and user-specific pages.
URL Parameters
Use angle brackets <variable_name> to capture URL segments. The captured value is passed to the view function as an argument.
@app.route('/user/')
def show_user(username):
return f'Hello, {username}!'
# /user/john -> 'Hello, john!'
# /user/jane -> 'Hello, jane!'
@app.route('/post/')
def show_post(post_id):
return f'Showing post #{post_id}'
# /post/42 -> 'Showing post #42'
Dynamic segments are captured and passed as function arguments. The variable name in the route must match the function parameter name.
URL Converters
Flask provides type converters to validate and convert URL parameters automatically.
| Converter | Description | Example |
|---|---|---|
string |
Default, accepts any text without slashes | <username> |
int |
Accepts positive integers | <int:user_id> |
float |
Accepts positive floating point values | <float:price> |
path |
Like string but accepts slashes | <path:filepath> |
uuid |
Accepts UUID strings | <uuid:item_id> |
@app.route('/product/')
def product(product_id):
# product_id is already an integer
return f'Product: {product_id}, Type: {type(product_id)}'
@app.route('/files/')
def serve_file(filepath):
# filepath can contain slashes: 'docs/images/logo.png'
return f'Serving: {filepath}'
Type converters ensure URLs are valid and convert values to the appropriate Python type. Invalid URLs (e.g., /product/abc for int) return 404.
Multiple Parameters
@app.route('/user//post/')
def user_post(username, post_id):
return f'Post #{post_id} by {username}'
# /user/john/post/5 -> 'Post #5 by john'
HTTP Methods
HTTP methods define the action to perform on a resource. Flask routes respond to GET requests by default, but you can specify other methods to handle form submissions and API operations.
GET Request
- Retrieves data from the server
- Parameters visible in URL
- Can be bookmarked
- Cached by browsers
- Safe and idempotent
POST Request
- Sends data to the server
- Parameters in request body
- Cannot be bookmarked
- Not cached
- Used for form submissions
Specifying HTTP Methods
from flask import Flask, request
app = Flask(__name__)
# Handle both GET and POST
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
# Process form submission
username = request.form.get('username')
return f'Logging in as {username}'
# Show login form
return 'Login Form (GET request)'
The methods parameter accepts a list of HTTP methods. Use request.method to determine which method was used.
RESTful Route Example
from flask import Flask, request, jsonify
app = Flask(__name__)
users = {}
@app.route('/api/users', methods=['GET'])
def get_users():
return jsonify(list(users.values()))
@app.route('/api/users', methods=['POST'])
def create_user():
data = request.get_json()
user_id = len(users) + 1
users[user_id] = {'id': user_id, 'name': data['name']}
return jsonify(users[user_id]), 201
RESTful APIs typically use GET for reading, POST for creating, PUT for updating, and DELETE for removing resources.
Request-Response Cycle
Understanding the request object and how to build responses is fundamental to Flask development. Flask provides powerful objects for handling incoming data and crafting outgoing responses.
The Request Object
The request object contains all the data sent by the client, including form data, query parameters, JSON payloads, headers, and files.
from flask import Flask, request
app = Flask(__name__)
@app.route('/search')
def search():
# Query parameters: /search?q=flask&page=1
query = request.args.get('q', '')
page = request.args.get('page', 1, type=int)
return f'Searching for: {query}, Page: {page}'
@app.route('/submit', methods=['POST'])
def submit():
name = request.form.get('name')
email = request.form.get('email')
return f'Received: {name} - {email}'
Use request.args for query parameters (GET) and request.form for form data (POST). The get() method provides defaults for missing values.
Common Request Attributes
| Attribute | Description | Example Use |
|---|---|---|
request.args |
URL query parameters | request.args.get('page') |
request.form |
Form data (POST) | request.form.get('email') |
request.json |
JSON payload | request.json['name'] |
request.method |
HTTP method used | if request.method == 'POST' |
request.headers |
Request headers | request.headers.get('Authorization') |
Building Responses
from flask import Flask, jsonify, make_response, redirect, url_for
app = Flask(__name__)
@app.route('/api/data')
def api_data():
return jsonify({'status': 'success', 'data': [1, 2, 3]})
@app.route('/custom')
def custom_response():
response = make_response('Custom Response', 201)
response.headers['X-Custom-Header'] = 'CustomValue'
return response
@app.route('/old-page')
def old_page():
return redirect(url_for('home'))
Flask provides helpers for common response types: jsonify() for JSON, make_response() for custom responses, and redirect() for redirections.
Practice Exercises
Apply what you have learned with these hands-on coding exercises. Start with the basics and work your way up to more complex challenges.
Flask Basics Practice
Task: Create a Flask application with a single route "/" that returns "Welcome to Flask!"
Show Solution
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return 'Welcome to Flask!'
if __name__ == '__main__':
app.run(debug=True)
Task: Add routes for /about, /contact, and /services that return appropriate messages.
Show Solution
@app.route('/about')
def about():
return 'About Us - We are a Flask company!'
@app.route('/contact')
def contact():
return 'Contact us at: hello@flask.com'
@app.route('/services')
def services():
return 'Our Services: Web Development, APIs, More'
Task: Create a route /greet/<name> that returns a personalized greeting like "Hello, John!"
Show Solution
@app.route('/greet/')
def greet(name):
return f'Hello, {name.capitalize()}!'
# Test: /greet/john -> "Hello, John!"
Task: Create a route /add/<int:a>/<int:b> that returns the sum of two numbers.
Show Solution
@app.route('/add//')
def add(a, b):
result = a + b
return f'{a} + {b} = {result}'
# Test: /add/5/3 -> "5 + 3 = 8"
Task: Create a /search route that accepts 'q' and 'limit' query parameters and returns them.
Show Solution
from flask import request
@app.route('/search')
def search():
query = request.args.get('q', 'No query')
limit = request.args.get('limit', 10, type=int)
return f'Searching: "{query}" with limit: {limit}'
# Test: /search?q=flask&limit=5
Task: Create a /login route that shows "Login Form" on GET and processes credentials on POST.
Show Solution
from flask import request
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
if username and password:
return f'Welcome, {username}!'
return 'Missing credentials', 400
return 'Login Form - Please POST credentials'
Task: Create a /api/user/<int:id> route that returns user data as JSON with name and email.
Show Solution
from flask import jsonify
users_db = {
1: {'name': 'Alice', 'email': 'alice@example.com'},
2: {'name': 'Bob', 'email': 'bob@example.com'}
}
@app.route('/api/user/')
def get_user(id):
user = users_db.get(id)
if user:
return jsonify({'id': id, **user})
return jsonify({'error': 'User not found'}), 404
Task: Create a POST /api/items endpoint that accepts JSON data and adds it to a list.
Show Solution
from flask import request, jsonify
items = []
@app.route('/api/items', methods=['GET', 'POST'])
def handle_items():
if request.method == 'POST':
data = request.get_json()
if not data or 'name' not in data:
return jsonify({'error': 'Name required'}), 400
item = {'id': len(items) + 1, 'name': data['name']}
items.append(item)
return jsonify(item), 201
return jsonify(items)
Task: Create an /old-home route that redirects to /home using url_for for dynamic URL generation.
Show Solution
from flask import redirect, url_for
@app.route('/home')
def home():
return 'Welcome to the new home page!'
@app.route('/old-home')
def old_home():
return redirect(url_for('home'))
# Visiting /old-home redirects to /home
Key Takeaways
Lightweight Framework
Flask is a micro framework that gives you the essentials without imposing structure - add only what you need
Declarative Routing
The @app.route decorator maps URL patterns to Python functions in a clean, readable way
Dynamic URLs
Capture URL segments with converters like <int:id> to build flexible, RESTful routes
HTTP Methods
Handle GET for reading, POST for creating - use request.method to differentiate in views
Request Object
Access form data, query params, JSON, and headers through the request object
Response Helpers
Use jsonify() for APIs, make_response() for custom headers, redirect() for navigation
Knowledge Check
Quick Quiz
Test what you've learned about Flask basics