🌶️ Why Flask?
- Understand the Request-Response cycle
- Create routes with decorators (`@app.route`)
- Render dynamic HTML using Jinja2 templates
- Build RESTful APIs that return JSON
- Organize larger applications with Blueprints
@app.route('/home') connects /home to a view function.
{{ variable }}) that Flask fills with dynamic data before sending to the browser.
jsonify() to serialize Python dicts to JSON responses.
Flask App: The restaurant itself.
Routes: Menu items. Each URL path is like ordering a dish.
View Functions: Chefs. They receive orders (requests), cook (process data), and serve (return responses).
Templates: Plate presentations. The chef fills the plate with food (data) before serving.
JSON API: Takeout orders. Instead of a plated meal (HTML page), you get raw ingredients in a box (JSON data) to use elsewhere.
Blueprints: Separate kitchen stations (appetizers, entrees, desserts). Each station handles its own menu section independently.
Flask is "micro" because it doesn't require particular tools or libraries. It has no database abstraction layer, form validation, or any other pre-existing third-party library common functions.
Start with Routes: Master @app.route() decorators first. Understand how URLs map to functions before adding complexity.
Use Debug Mode: Always run with debug=True during development. Auto-reload saves time, and error pages show helpful tracebacks.
Test Routes with curl/Postman: Don't rely only on browsers. Use curl or Postman to test API routes and inspect raw responses.
Organize Early: Use Blueprints from the start for larger apps. Refactoring later is painful.
Learn Jinja2 Syntax: Templates are powerful. Master {{ }}, {% %}, {# #} for variables, logic, and comments.
🛤️ Routes & Views
Mapping URLs to Python functions.
# ❌ BAD: Returning HTML strings directly
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
# Nightmare to maintain!
return "<html><body><h1>Hello World</h1></body></html>"
# 🔰 NOVICE: Variable Rules
from flask import Flask
app = Flask(__name__)
# <name> captures the URL part
@app.route('/user/<name>')
def user_profile(name):
return f"Hello, {name}!"
# ⭐ BEST PRACTICE: Explicit Types
from flask import Flask
app = Flask(__name__)
# Ensures 'post_id' is an integer. 404s if string provided.
@app.route('/post/<int:post_id>')
def show_post(post_id):
return f"Post ID: {post_id} (Type: {type(post_id).__name__})"
Description: Create a Flask application with multiple interconnected pages.
- Create a route for the Home page (
/) that displays a welcome message - Create a route for the About page (
/about) with information about your site - Create a route for the Contact page (
/contact) with contact details - Add navigation links between all pages
- Use type converters for a User Profile page (
/user/<int:user_id>) - Bonus: Add a dynamic
/products/<category>route that displays different products based on category
# Starter Code
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return "Welcome to my website!"
# TODO: Add /about, /contact, /user/ routes
if __name__ == '__main__':
app.run(debug=True)
🎨 Jinja2 Templates
Separate your logic (Python) from your presentation (HTML).
<!-- templates/index.html -->
<h1>Welcome, {{ user.name }}!</h1>
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% if user.is_admin %}
<button>Delete Everything</button>
{% endif %}
from flask import render_template
@app.route('/')
def index():
user = {'name': 'Alice', 'is_admin': True}
items = ['Apple', 'Banana']
# Pass variables to the template
return render_template('index.html', user=user, items=items)
Description: Build a blog page using Jinja2 templates with loops and conditionals.
- Create a
templates/blog.htmltemplate - Use a for loop (
{% for post in posts %}) to display multiple blog posts - Use conditionals (
{% if post.featured %}) to highlight featured posts - Display post title, author, date, and content using
{{ variable }}syntax - Use template inheritance with a
base.htmllayout - Bonus: Add a filter (
{{ post.content | truncate(100) }}) to show post previews
<!-- templates/blog.html -->
{% extends 'base.html' %}
{% block content %}
<h1>Blog Posts</h1>
{% for post in posts %}
<article class="{% if post.featured %}featured{% endif %}">
<h2>{{ post.title }}</h2>
<p>By {{ post.author }} on {{ post.date }}</p>
<p>{{ post.content | truncate(150) }}</p>
</article>
{% else %}
<p>No posts available.</p>
{% endfor %}
{% endblock %}
Description: Build a user registration form with server-side validation.
- Create an HTML form with fields: username, email, password, confirm password
- Handle both
GET(display form) andPOST(process form) methods - Validate that username is at least 3 characters
- Validate email format using basic checks or regex
- Ensure password and confirm password match
- Display error messages on the form if validation fails
- Bonus: Add CSRF protection using Flask-WTF
from flask import Flask, render_template, request, flash, redirect
app = Flask(__name__)
app.secret_key = 'your-secret-key'
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'POST':
username = request.form['username']
email = request.form['email']
password = request.form['password']
confirm = request.form['confirm']
# TODO: Add validation logic
if len(username) < 3:
flash('Username must be at least 3 characters', 'error')
elif password != confirm:
flash('Passwords do not match', 'error')
else:
flash('Registration successful!', 'success')
return redirect('/login')
return render_template('register.html')
🔌 JSON APIs
Building backends for React, Vue, or Mobile Apps.
from flask import jsonify, request
@app.route('/api/data', methods=['GET', 'POST'])
def api_handler():
if request.method == 'POST':
# Get JSON sent by client
payload = request.get_json()
return jsonify({'status': 'received', 'you_sent': payload}), 201
# Default to GET
data = [1, 2, 3]
return jsonify({'status': 'ok', 'data': data})
Description: Create a complete RESTful API for managing a resource (e.g., books, tasks, or products).
- GET /api/items - Return a list of all items as JSON
- GET /api/items/<int:id> - Return a single item by ID (404 if not found)
- POST /api/items - Create a new item from JSON body, return 201 status
- PUT /api/items/<int:id> - Update an existing item completely
- PATCH /api/items/<int:id> - Partially update an item
- DELETE /api/items/<int:id> - Delete an item, return 204 No Content
- Use proper HTTP status codes (200, 201, 204, 400, 404)
- Bonus: Add pagination for the GET all endpoint
from flask import Flask, jsonify, request, abort
app = Flask(__name__)
# In-memory storage
items = [
{'id': 1, 'name': 'Item One', 'price': 19.99},
{'id': 2, 'name': 'Item Two', 'price': 29.99},
]
next_id = 3
# GET all items
@app.route('/api/items', methods=['GET'])
def get_items():
return jsonify({'items': items})
# GET single item
@app.route('/api/items/<int:item_id>', methods=['GET'])
def get_item(item_id):
item = next((i for i in items if i['id'] == item_id), None)
if item is None:
abort(404)
return jsonify(item)
# TODO: Implement POST, PUT, PATCH, DELETE
🏗️ Project Structure
Don't put everything in app.py.
my_app/
├── app.py # Entry point
├── config.py # Settings
├── templates/ # HTML
├── static/ # CSS/JS
└── blueprints/ # Features
├── auth.py # Login/Register routes
└── shop.py # Shopping cart routes
from flask import Blueprint
# Create a blueprint named 'auth'
auth_bp = Blueprint('auth', __name__)
@auth_bp.route('/login')
def login():
return "Login Page"
from flask import Flask
from blueprints.auth import auth_bp
app = Flask(__name__)
# Register the blueprint
# All routes in auth_bp will be prefixed with /auth
app.register_blueprint(auth_bp, url_prefix='/auth')
Description: Connect your Flask app to a SQLite database and implement CRUD operations.
- Install Flask-SQLAlchemy:
pip install flask-sqlalchemy - Configure SQLite database connection in your app
- Create a Model class (e.g.,
TaskorUser) with columns - Implement Create: Add new records to the database
- Implement Read: Query and display all records
- Implement Update: Modify existing records
- Implement Delete: Remove records from the database
- Bonus: Add a search/filter feature using query parameters
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///tasks.db'
db = SQLAlchemy(app)
# Define a Model
class Task(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
completed = db.Column(db.Boolean, default=False)
def to_dict(self):
return {'id': self.id, 'title': self.title, 'completed': self.completed}
# Create tables
with app.app_context():
db.create_all()
# CRUD Operations
@app.route('/tasks', methods=['GET'])
def get_tasks():
tasks = Task.query.all()
return jsonify([task.to_dict() for task in tasks])
@app.route('/tasks', methods=['POST'])
def create_task():
data = request.get_json()
task = Task(title=data['title'])
db.session.add(task)
db.session.commit()
return jsonify(task.to_dict()), 201
# TODO: Implement GET by ID, PUT/PATCH, DELETE
📜 Flask Cheat Sheet
# ═══ APP SETUP ═══
from flask import Flask
app = Flask(__name__)
# ═══ ROUTES ═══
@app.route('/')
@app.route('/user/<int:id>')
@app.route('/submit', methods=['POST'])
# ═══ REQUEST / RESPONSE ═══
from flask import request, jsonify, render_template
val = request.args.get('key') # URL query param
val = request.form['key'] # Form data
json = request.get_json() # JSON body
return render_template('page.html', var=val)
return jsonify({'status': 'ok'})
# ═══ RUNNING ═══
if __name__ == '__main__':
app.run(debug=True)
- Routes Map URLs to Functions:
@app.route('/path')decorator connects URL paths to view functions. This is the heart of Flask. - Templates Separate Logic from Presentation: Use Jinja2 templates to keep HTML separate from Python.
render_template()fills placeholders with data. - JSON APIs with jsonify(): Return structured data with
jsonify(dict). Essential for building backends that serve mobile apps or SPAs. - Use Blueprints for Organization: Large apps need modular structure. Blueprints group related routes like mini-applications.
- Debug Mode is Your Friend:
app.run(debug=True)enables auto-reload and detailed error pages. Never use in production. - HTTP Methods Matter:
@app.route('/path', methods=['GET', 'POST'])controls what requests your route accepts. GET retrieves, POST submits data. - Test with Tools: Use curl, Postman, or Python
requeststo test API endpoints. Don't rely solely on browser testing. - Static Files in /static, Templates in /templates: Flask convention. CSS/JS go in
static/, HTML templates intemplates/.