🌶️ Why Flask?

🎯 Learning Objectives
  • 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
📚 Key Vocabulary
Route URL path mapped to a Python function. @app.route('/home') connects /home to a view function.
View Function Python function that handles requests and returns responses (HTML, JSON, redirects).
Template HTML file with Jinja2 placeholders ({{ variable }}) that Flask fills with dynamic data before sending to the browser.
JSON API Route that returns structured data (JSON) instead of HTML. Use jsonify() to serialize Python dicts to JSON responses.
Blueprint Modular component for organizing routes. Like mini-applications that can be plugged into the main Flask app.
Request-Response Cycle Browser sends HTTP request → Flask routes to view function → function returns response (HTML/JSON) → browser displays it.
🎯 Analogy: Flask as Restaurant Service

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.

💡 Microframework

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.

💡 Learning Strategy: Flask Web Development

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.

Python ❌ Bad Practice Hardcoded HTML
# ❌ 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>"
Python 🔰 Novice Dynamic Route
# 🔰 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}!"
Python ⭐ Best Practice Type Converters
# ⭐ 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__})"
🏋️ Exercise: Multi-Page Website

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).

HTML + Jinja ⭐ Logic in 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 %}
Python ⭐ Rendering
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)
🏋️ Exercise: Dynamic Blog

Description: Build a blog page using Jinja2 templates with loops and conditionals.

  • Create a templates/blog.html template
  • 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.html layout
  • 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 %}
🏋️ Exercise: User Registration

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) and POST (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.

Python ⭐ RESTful
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})
🚀 Challenge: REST API

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.

📂 Blueprint Pattern
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
                    
Python (blueprints/auth.py) ⭐ Modular
from flask import Blueprint

# Create a blueprint named 'auth'
auth_bp = Blueprint('auth', __name__)

@auth_bp.route('/login')
def login():
    return "Login Page"
Python (app.py) ⭐ Registration
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')
🏋️ Exercise: Flask-SQLAlchemy CRUD

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., Task or User) 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

Patterns Reference
# ═══ 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)
🎯 Key Takeaways: Flask Web Development
  • 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 requests to 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 in templates/.