0% found this document useful (0 votes)
2 views39 pages

Flask Complete Notes

This document serves as a comprehensive guide to Flask, a lightweight Python web framework, covering topics from basic setup to advanced features. It includes sections on routing, request and response handling, templating with Jinja2, database integration, user authentication, and deployment strategies. The notes also provide practical examples and best practices for developing Flask applications.

Uploaded by

kedahedipatheda
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views39 pages

Flask Complete Notes

This document serves as a comprehensive guide to Flask, a lightweight Python web framework, covering topics from basic setup to advanced features. It includes sections on routing, request and response handling, templating with Jinja2, database integration, user authentication, and deployment strategies. The notes also provide practical examples and best practices for developing Flask applications.

Uploaded by

kedahedipatheda
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

FLASK — Complete Learning Notes Page 1

FLASK
Complete Learning Notes — Basic to Advanced

Web Framework • RESTful APIs • Databases • Authentication


Templates • Blueprints • Deployment • Testing • Best Practices

Generated: May 18, 2026

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 2

Table of Contents
1. Introduction to Flask
— What is Flask?
— WSGI & Werkzeug
— Flask vs Django
— Installation & Setup
2. Your First Flask App
— Hello World
— App Object
— Debug Mode
— Running the Server
3. Routing
— Basic Routes
— Dynamic URLs
— URL Converters
— url_for()
— HTTP Methods
4. Request & Response
— request Object
— response Object
— Query Strings
— Form Data
— JSON
5. Jinja2 Templates
— Rendering Templates
— Variables
— Filters
— Control Flow
— Template Inheritance
— Macros
6. Static Files
— Serving CSS/JS/Images
— url_for Static
— File Organisation
7. Forms & Validation
— HTML Forms
— Flask-WTF
— CSRF Protection
— Validators
— File Uploads
8. Databases with Flask-SQLAlchemy
— Setup
— Models
— CRUD Operations

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 3

— Relationships
— Migrations (Flask-Migrate)
9. User Authentication
— Flask-Login
— Password Hashing
— Login/Logout
— Protected Routes
— Remember Me
10. Sessions & Cookies
— Session Basics
— Secure Cookies
— Flash Messages
11. Blueprints & Application Factory
— Why Blueprints
— Creating Blueprints
— Registering
— App Factory Pattern
12. REST APIs with Flask
— REST Principles
— JSON Responses
— Status Codes
— Flask-RESTful
— Postman Testing
13. Error Handling
— Custom Error Pages
— Error Handlers
— Logging
14. Middleware & Hooks
— before_request
— after_request
— teardown
— g Object
15. Configuration
— Config Classes
— Environment Variables
— Flask-Dotenv
16. Flask-Mail & Email
— Setup
— Sending Emails
— HTML Emails
— Async Email
17. Flask-Caching
— Setup
— Cache Decorators

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 4

— Cache Backends
18. Testing Flask Apps
— unittest
— Flask Test Client
— Fixtures
— Testing APIs
19. Deployment
— Production WSGI (Gunicorn)
— Nginx
— Docker
— Heroku / Render
— Environment Setup
20. Advanced Patterns
— Context Locals
— Signals
— CLI Commands
— Extensions
— Project Structure

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 5

Chapter 1: Introduction to Flask


1.1 What is Flask?
Flask is a lightweight, micro web framework for Python created by Armin Ronacher in 2010. It is called a 'micro'
framework because it keeps the core simple and extensible — it does not include a database abstraction layer,
form validation, or any other components where third-party libraries can do the job.

Flask is built on two key libraries:

• Werkzeug — a WSGI (Web Server Gateway Interface) utility library that handles routing, request/response
objects, and low-level HTTP
• Jinja2 — a powerful templating engine used to render HTML dynamically

1.2 WSGI Explained


WSGI (Web Server Gateway Interface) is a specification (PEP 3333) that defines how a web server communicates
with a Python web application. Your Flask app is a WSGI application — a callable that accepts the environment
dict and a start_response callable.
# Minimal raw WSGI application (Flask does all this for you)
def simple_app(environ, start_response):
status = '200 OK'
headers = [('Content-Type', 'text/plain')]
start_response(status, headers)
return [b'Hello from WSGI!']

1.3 Flask vs Django


Feature Flask Django

Type Micro-framework Full-stack framework

ORM Optional (SQLAlchemy) Built-in

Admin Panel Not built-in Auto-generated

Learning Curve Gentle Steeper

Flexibility Very high Opinionated

Use Case APIs, small-medium apps Large, content-heavy apps

1.4 Installation & Setup


# Create and activate virtual environment
python -m venv venv
source venv/bin/activate # Linux/macOS
venv\Scripts\activate # Windows
# Install Flask
pip install flask

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 6

# Verify installation
python -c "import flask; print(flask.__version__)"
# Common Flask extensions to install
pip install flask-sqlalchemy flask-migrate flask-login
pip install flask-wtf flask-mail flask-restful

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 7

Chapter 2: Your First Flask App


2.1 Hello World
# [Link]
from flask import Flask
app = Flask(__name__) # create the Flask application
@[Link]('/') # register route for URL '/'
def index():
return 'Hello, World!'
if __name__ == '__main__':
[Link](debug=True)
$ python [Link]
* Running on [Link]
* Debug mode: ON

2.2 The Flask App Object


Flask(__name__) creates the application instance. The __name__ argument tells Flask where to look for
templates, static files, and other resources.
# Flask constructor parameters
app = Flask(
__name__,
template_folder='templates', # default
static_folder='static', # default
static_url_path='/static', # default URL prefix
)

2.3 Debug Mode & Configuration


# Method 1: in code (never use in production!)
[Link](debug=True, host='[Link]', port=5000)
# Method 2: environment variables (recommended)
# export FLASK_APP=[Link]
# export FLASK_DEBUG=1
# flask run
# Method 3: config object
[Link]['DEBUG'] = True
■■ Warning: Never run debug=True in production. It exposes an interactive debugger to anyone.

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 8

Chapter 3: Routing
3.1 Basic Routes
Routes map URL patterns to view functions. The @[Link]() decorator registers the function as a handler for
that URL.
from flask import Flask
app = Flask(__name__)
@[Link]('/')
def home(): return 'Home Page'
@[Link]('/about')
def about(): return 'About Page'
@[Link]('/contact')
def contact(): return 'Contact Page'

3.2 Dynamic URL Parameters


# <variable_name> captures part of the URL
@[Link]('/user/<username>')
def user_profile(username):
return f'Profile of {username}'
# URL Converters
@[Link]('/post/<int:post_id>')
def show_post(post_id): # post_id is an int
return f'Post #{post_id}'
@[Link]('/price/<float:amount>')
def show_price(amount):
return f'Price: {amount:.2f}'
@[Link]('/path/<path:subpath>')
def show_subpath(subpath): # allows slashes in URL
return f'Subpath: {subpath}'

Converter Type Example URL Result

<string:x> str (default) '/hello' x = 'hello'

<int:x> int '/42' x = 42

<float:x> float '/3.14' x = 3.14

<path:x> str with / '/a/b/c' x = 'a/b/c'

<uuid:x> UUID '/uuid-here' x = UUID(...)

3.3 url_for() — Reverse URL Building


url_for() generates URLs dynamically by function name. This is the correct way to build links — never hardcode
URLs.
from flask import url_for

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 9

@[Link]('/login')
def login(): pass
@[Link]('/user/<int:id>')
def profile(id): pass
# Inside a view or template:
url_for('login') # → '/login'
url_for('profile', id=5) # → '/user/5'
url_for('static', filename='[Link]') # → '/static/[Link]'
# External URL (for emails, etc.)
url_for('login', _external=True) # → '[Link]

3.4 HTTP Methods


from flask import request
@[Link]('/login', methods=['GET', 'POST'])
def login():
if [Link] == 'POST':
# handle form submission
username = [Link]('username')
return f'Logged in as {username}'
return '''
<form method='post'>
<input name='username'>
<button>Login</button>
</form>
'''
# Separate handlers using add_url_rule
@[Link]('/items') # GET only — Flask 2.0+
def get_items(): pass
@[Link]('/items') # POST only
def create_item(): pass

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 10

Chapter 4: Request & Response Objects


4.1 The request Object
Flask's request object (from flask import request) provides access to all incoming HTTP data.
from flask import request
@[Link]('/demo', methods=['GET', 'POST'])
def demo():
# URL query string: /demo?page=2&sort=name
page = [Link]('page', 1, type=int)
sort = [Link]('sort', 'id')
# Form data (POST, Content-Type: application/x-www-form-urlencoded)
name = [Link]('name')
email = [Link]('email')
# JSON body (Content-Type: application/json)
data = request.get_json()
# Headers
auth = [Link]('Authorization')
# Cookies
token = [Link]('session_token')
# Files
file = [Link]('upload')
# Request metadata
print([Link]) # 'GET', 'POST', etc.
print([Link]) # full URL
print([Link]) # URL path only
print([Link]) # hostname
print(request.remote_addr) # client IP
return 'OK'

4.2 Building Responses


from flask import make_response, jsonify, redirect, abort
# Simple string response
@[Link]('/text')
def text(): return 'Hello'
# With custom status code
@[Link]('/created')
def created(): return 'Resource Created', 201
# With headers
@[Link]('/custom')
def custom():
resp = make_response('Custom Response', 200)
[Link]['X-Custom-Header'] = 'value'
resp.set_cookie('user', 'alice')
return resp
# JSON response

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 11

@[Link]('/api/user')
def api_user():
return jsonify({'name': 'Alice', 'age': 25})
# Redirect
@[Link]('/old')
def old(): return redirect('/new')
# Abort with error
@[Link]('/secret')
def secret():
abort(403) # raises 403 Forbidden

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 12

Chapter 5: Jinja2 Templates


Jinja2 is Flask's built-in templating engine. Templates are HTML files stored in the templates/ folder that can
include dynamic content using special syntax.

5.1 Rendering Templates


from flask import render_template
@[Link]('/')
def index():
user = {'name': 'Alice', 'age': 25}
items = ['Laptop', 'Phone', 'Tablet']
return render_template('[Link]', user=user, items=items)
<!-- templates/[Link] -->
<!DOCTYPE html>
<html>
<head><title>{{ [Link] }}'s Page</title></head>
<body>
<h1>Hello, {{ [Link] }}!</h1>
<p>Age: {{ [Link] }}</p>
<!-- Loop -->
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
<!-- Condition -->
{% if [Link] >= 18 %}
<p>You are an adult.</p>
{% else %}
<p>You are a minor.</p>
{% endif %}
</body>
</html>

5.2 Jinja2 Filters


Filter Example Output

upper {{ 'hello'|upper }} HELLO

lower {{ 'HELLO'|lower }} hello

title {{ 'hello world'|title }} Hello World

length {{ [1,2,3]|length }} 3

default {{ x|default('N/A') }} N/A if x is undefined

truncate {{ text|truncate(30) }} First 30 chars...

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 13

safe {{ html|safe }} Renders raw HTML

join {{ list|join(', ') }} item1, item2

sort {% for x in list|sort %} Sorted iteration

5.3 Template Inheritance


Template inheritance lets you define a base layout and extend it. This avoids repeating HTML boilerplate on every
page.
<!-- templates/[Link] -->
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}My Site{% endblock %}</title>
<link rel='stylesheet' href='{{ url_for('static', filename='[Link]') }}'>
</head>
<body>
<nav>
<a href='{{ url_for('index') }}'>Home</a> |
<a href='{{ url_for('about') }}'>About</a>
</nav>
<main>
{% block content %}{% endblock %}
</main>
<footer>&copy; 2025 My App</footer>
</body>
</html>
<!-- templates/[Link] -->
{% extends '[Link]' %}
{% block title %}Home — My Site{% endblock %}
{% block content %}
<h1>Welcome to My Site!</h1>
<p>This content replaces the block.</p>
{% endblock %}

5.4 Template Macros


<!-- templates/[Link] -->
{% macro input_field(name, label, type='text') %}
<div class='form-group'>
<label for='{{ name }}'>{{ label }}</label>
<input type='{{ type }}' id='{{ name }}' name='{{ name }}'>
</div>
{% endmacro %}
<!-- Usage in another template -->
{% from '[Link]' import input_field %}
{{ input_field('email', 'Email Address', type='email') }}

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 14

{{ input_field('password', 'Password', type='password') }}

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 15

Chapter 6: Static Files


Static files (CSS, JavaScript, images, fonts) are served from the static/ folder in your project root.

6.1 Project Structure


myapp/
■■■ [Link]
■■■ static/
■ ■■■ css/
■ ■ ■■■ [Link]
■ ■■■ js/
■ ■ ■■■ [Link]
■ ■■■ img/
■ ■■■ [Link]
■■■ templates/
■■■ [Link]

6.2 Referencing Static Files


<!-- Always use url_for for static files — portable, cache-busting friendly -->
<link rel='stylesheet' href='{{ url_for('static', filename='css/[Link]') }}'>
<script src='{{ url_for('static', filename='js/[Link]') }}'></script>
<img src='{{ url_for('static', filename='img/[Link]') }}' alt='Logo'>
■ Tip: Use url_for('static', ...) instead of hardcoded /static/ paths — it works across different deployment contexts.

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 16

Chapter 7: Forms & Validation


7.1 Flask-WTF Setup
pip install flask-wtf
# [Link]
from flask import Flask
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from [Link] import DataRequired, Email, Length, EqualTo
app = Flask(__name__)
[Link]['SECRET_KEY'] = 'your-secret-key-here' # required for CSRF

7.2 Creating Forms


class RegistrationForm(FlaskForm):
username = StringField('Username',
validators=[DataRequired(), Length(min=3, max=20)])
email = StringField('Email',
validators=[DataRequired(), Email()])
password = PasswordField('Password',
validators=[DataRequired(), Length(min=6)])
confirm = PasswordField('Confirm Password',
validators=[EqualTo('password')])
submit = SubmitField('Register')
@[Link]('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit(): # valid POST
username = [Link]
email = [Link]
# save user to database...
return redirect(url_for('login'))
return render_template('[Link]', form=form)

7.3 Rendering Forms in Templates


<!-- templates/[Link] -->
{% extends '[Link]' %}
{% block content %}
<form method='POST'>
{{ form.hidden_tag() }} {# CSRF token — required! #}
<div>
{{ [Link] }}
{{ [Link](class='form-control') }}
{% for error in [Link] %}
<span class='error'>{{ error }}</span>
{% endfor %}
</div>

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 17

{{ [Link] }} {{ [Link]() }}
{{ [Link] }} {{ [Link]() }}
{{ [Link]() }}
</form>
{% endblock %}

7.4 File Uploads


from flask import request
from [Link] import secure_filename
import os
UPLOAD_FOLDER = 'uploads'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'pdf'}
[Link]['UPLOAD_FOLDER'] = UPLOAD_FOLDER
[Link]['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16 MB limit
def allowed_file(filename):
return '.' in filename and \
[Link]('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@[Link]('/upload', methods=['GET', 'POST'])
def upload_file():
if [Link] == 'POST':
file = [Link]('file')
if file and allowed_file([Link]):
filename = secure_filename([Link])
[Link]([Link]([Link]['UPLOAD_FOLDER'], filename))
return f'Uploaded: {filename}'
return render_template('[Link]')

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 18

Chapter 8: Databases with Flask-SQLAlchemy


8.1 Setup
pip install flask-sqlalchemy flask-migrate
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
[Link]['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///[Link]'
[Link]['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

8.2 Defining Models


class User([Link]):
id = [Link]([Link], primary_key=True)
username = [Link]([Link](80), unique=True, nullable=False)
email = [Link]([Link](120), unique=True, nullable=False)
created = [Link]([Link], default=[Link]())
posts = [Link]('Post', backref='author', lazy=True)
def __repr__(self):
return f'<User {[Link]}>'
class Post([Link]):
id = [Link]([Link], primary_key=True)
title = [Link]([Link](200), nullable=False)
body = [Link]([Link], nullable=False)
user_id = [Link]([Link], [Link]('[Link]'), nullable=False)
# Create all tables
with app.app_context():
db.create_all()

8.3 CRUD Operations


# CREATE — add a new record
user = User(username='alice', email='alice@[Link]')
[Link](user)
[Link]()
# READ — query records
all_users = [Link]()
alice = [Link].filter_by(username='alice').first()
user_by_id = [Link](1)
# Filtering & ordering
users = [Link]([Link]('%ali%')).all()
users = [Link].order_by([Link]).limit(10).all()
count = [Link]()
# UPDATE — modify a record
[Link] = 'newalice@[Link]'
[Link]()

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 19

# DELETE — remove a record


[Link](alice)
[Link]()

8.4 Database Relationships


# One-to-Many (User has many Posts)
alice = [Link].filter_by(username='alice').first()
post = Post(title='Hello Flask', body='My first post', author=alice)
[Link](post)
[Link]()
# Access related objects
print([Link]) # all posts by alice
print([Link]) # the user who wrote the post
# Many-to-Many helper table
post_tags = [Link]('post_tags',
[Link]('post_id', [Link], [Link]('[Link]')),
[Link]('tag_id', [Link], [Link]('[Link]'))
)
class Tag([Link]):
id = [Link]([Link], primary_key=True)
name = [Link]([Link](50))
posts = [Link]('Post', secondary=post_tags, backref='tags')

8.5 Migrations with Flask-Migrate


from flask_migrate import Migrate
migrate = Migrate(app, db)
# Shell commands to manage migrations
flask db init # initialize migrations folder (once)
flask db migrate -m 'add users table' # generate migration
flask db upgrade # apply migration
flask db downgrade # revert last migration
■ Always commit your migration files to version control.

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 20

Chapter 9: User Authentication


9.1 Setup Flask-Login
pip install flask-login
from flask_login import LoginManager, UserMixin, login_user,
logout_user, login_required, current_user
login_manager = LoginManager(app)
login_manager.login_view = 'login' # redirect here if not logged in
login_manager.login_message_category = 'info'
# User model must inherit UserMixin
class User([Link], UserMixin):
id = [Link]([Link], primary_key=True)
username = [Link]([Link](80), unique=True)
email = [Link]([Link](120), unique=True)
password_hash = [Link]([Link](256))
@login_manager.user_loader
def load_user(user_id):
return [Link](int(user_id))

9.2 Password Hashing


from [Link] import generate_password_hash, check_password_hash
# Hash password before storing
user.password_hash = generate_password_hash('mysecretpassword')
# Verify password
is_valid = check_password_hash(user.password_hash, 'mysecretpassword')
# → True

9.3 Login & Logout Views


@[Link]('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('dashboard'))
form = LoginForm()
if form.validate_on_submit():
user = [Link].filter_by(email=[Link]).first()
if user and check_password_hash(user.password_hash, [Link]):
login_user(user, remember=[Link])
next_page = [Link]('next') # redirect after login
return redirect(next_page or url_for('dashboard'))
flash('Invalid email or password', 'danger')
return render_template('[Link]', form=form)
@[Link]('/logout')
@login_required
def logout():
logout_user()
return redirect(url_for('index'))

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 21

# Protected route
@[Link]('/dashboard')
@login_required
def dashboard():
return render_template('[Link]', user=current_user)

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 22

Chapter 10: Sessions, Cookies & Flash Messages


10.1 Sessions
Flask sessions store data on the client side in a signed cookie. You need a SECRET_KEY to sign and verify the
session.
from flask import session
[Link]['SECRET_KEY'] = 'super-secret-key'
# Set session data
@[Link]('/set')
def set_session():
session['username'] = 'alice'
session['theme'] = 'dark'
return 'Session set'
# Read session data
@[Link]('/get')
def get_session():
username = [Link]('username', 'Guest')
return f'Hello, {username}'
# Delete session key
@[Link]('/clear')
def clear_session():
[Link]('username', None)
[Link]() # clear everything
return 'Session cleared'

10.2 Cookies
from flask import make_response, request
# Set a cookie
@[Link]('/setcookie')
def set_cookie():
resp = make_response('Cookie set!')
resp.set_cookie('theme', 'dark',
max_age=60*60*24*30, # 30 days
httponly=True,
samesite='Lax')
return resp
# Read a cookie
@[Link]('/getcookie')
def get_cookie():
theme = [Link]('theme', 'light')
return f'Theme: {theme}'

10.3 Flash Messages


from flask import flash, get_flashed_messages
@[Link]('/flash-demo')

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 23

def flash_demo():
flash('Profile updated successfully!', 'success')
flash('Please verify your email.', 'warning')
return redirect(url_for('index'))
<!-- In [Link] template -->
{% with messages = get_flashed_messages(with_categories=True) %}
{% for category, message in messages %}
<div class='alert alert-{{ category }}'>{{ message }}</div>
{% endfor %}
{% endwith %}

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 24

Chapter 11: Blueprints & Application Factory


Blueprints let you split a large app into reusable modules. The Application Factory pattern (create_app()) makes it
easy to configure the app for different environments (dev/test/prod).

11.1 Creating a Blueprint


# auth/[Link]
from flask import Blueprint, render_template, redirect, url_for
auth = Blueprint('auth', __name__, url_prefix='/auth')
@[Link]('/login')
def login():
return render_template('auth/[Link]')
@[Link]('/register')
def register():
return render_template('auth/[Link]')
@[Link]('/logout')
def logout():
return redirect(url_for('[Link]'))

11.2 Application Factory Pattern


# app/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
db = SQLAlchemy()
login_manager = LoginManager()
def create_app(config='[Link]'):
app = Flask(__name__)
[Link].from_object(config)
# Init extensions
db.init_app(app)
login_manager.init_app(app)
# Register blueprints
from .[Link] import auth
from .[Link] import main
from .[Link] import api
app.register_blueprint(auth)
app.register_blueprint(main)
app.register_blueprint(api, url_prefix='/api')
return app

11.3 Recommended Project Structure


myapp/
■■■ [Link] # entry point
■■■ [Link] # configuration classes
■■■ [Link]

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 25

■■■ app/
■ ■■■ __init__.py # create_app() factory
■ ■■■ [Link] # database models
■ ■■■ auth/
■ ■ ■■■ __init__.py
■ ■ ■■■ [Link]
■ ■ ■■■ [Link]
■ ■■■ main/
■ ■ ■■■ __init__.py
■ ■ ■■■ [Link]
■ ■■■ api/
■ ■ ■■■ __init__.py
■ ■ ■■■ [Link]
■ ■■■ static/
■ ■■■ templates/
■■■ migrations/

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 26

Chapter 12: REST APIs with Flask


12.1 REST Principles
• Stateless — each request contains all needed info
• Resource-based — URLs identify resources (/users/42)
• HTTP Methods — GET (read), POST (create), PUT/PATCH (update), DELETE (delete)
• JSON — standard data exchange format
• Status Codes — 200 OK, 201 Created, 400 Bad Request, 404 Not Found, 500 Server Error

12.2 Building a REST API


from flask import Flask, jsonify, request, abort
app = Flask(__name__)
# In-memory store for demo
users = {1: {'id':1,'name':'Alice','email':'alice@[Link]'},
2: {'id':2,'name':'Bob', 'email':'bob@[Link]'}}
next_id = 3
# GET all users
@[Link]('/api/users')
def get_users():
return jsonify(list([Link]()))
# GET single user
@[Link]('/api/users/<int:uid>')
def get_user(uid):
user = [Link](uid)
if not user: abort(404)
return jsonify(user)
# POST — create user
@[Link]('/api/users')
def create_user():
global next_id
data = request.get_json() or {}
if 'name' not in data or 'email' not in data:
abort(400)
user = {'id': next_id, 'name': data['name'], 'email': data['email']}
users[next_id] = user
next_id += 1
return jsonify(user), 201
# PUT — update user
@[Link]('/api/users/<int:uid>')
def update_user(uid):
if uid not in users: abort(404)
data = request.get_json() or {}
users[uid].update(data)
return jsonify(users[uid])
# DELETE user

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 27

@[Link]('/api/users/<int:uid>')
def delete_user(uid):
if uid not in users: abort(404)
del users[uid]
return '', 204

12.3 HTTP Status Codes


Code Meaning When to Use

200 OK Successful GET, PUT

201 Created Successful POST

204 No Content Successful DELETE

400 Bad Request Invalid input data

401 Unauthorized Not authenticated

403 Forbidden Authenticated but no permission

404 Not Found Resource doesn't exist

409 Conflict Duplicate resource

422 Unprocessable Entity Validation failed

500 Internal Server Error Unhandled exception

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 28

Chapter 13: Error Handling


13.1 Custom Error Pages
from flask import render_template
@[Link](404)
def not_found(e):
return render_template('errors/[Link]'), 404
@[Link](403)
def forbidden(e):
return render_template('errors/[Link]'), 403
@[Link](500)
def server_error(e):
return render_template('errors/[Link]'), 500
# Catch all HTTP exceptions
from [Link] import HTTPException
@[Link](HTTPException)
def handle_http_exception(e):
return jsonify(error=str(e)), [Link]

13.2 Logging
import logging
from [Link] import RotatingFileHandler
if not [Link]:
handler = RotatingFileHandler('logs/[Link]',
maxBytes=10*1024*1024, # 10 MB
backupCount=5)
[Link]([Link])
formatter = [Link](
'[%(asctime)s] %(levelname)s: %(message)s'
)
[Link](formatter)
[Link](handler)
# Usage inside views
[Link]('User logged in: %s', username)
[Link]('Invalid attempt from %s', request.remote_addr)
[Link]('Database error: %s', str(e))

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 29

Chapter 14: Middleware & Request Hooks


Flask provides hooks to run code before or after each request. This is useful for authentication checks, DB
connection management, logging, etc.
from flask import g, request, session
import time
@app.before_request
def before():
g.start_time = [Link]() # per-request storage
[Link] = [Link]('user_id') # load user from session
@app.after_request
def after(response):
elapsed = [Link]() - g.start_time
[Link]['X-Response-Time'] = f'{elapsed:.4f}s'
return response # must return response
@app.teardown_request
def teardown(error=None):
# Always runs — even on exception
[Link]() # clean up DB session
@app.before_app_request # Blueprint-level (runs for whole app)
def check_maintenance():
if [Link]('MAINTENANCE_MODE'):
abort(503)

14.1 The g Object


flask.g is a special object for storing data during a single request lifecycle. It is cleared at the end of each request.
from flask import g
def get_db():
if 'db' not in g:
[Link] = connect_database()
return [Link]
@app.teardown_appcontext
def close_db(error):
db = [Link]('db', None)
if db: [Link]()

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 30

Chapter 15: Configuration Management


15.1 Config Classes
# [Link]
import os
class Config:
SECRET_KEY = [Link]('SECRET_KEY') or 'dev-key-change-me'
SQLALCHEMY_TRACK_MODIFICATIONS = False
MAIL_SERVER = '[Link]'
MAIL_PORT = 587
MAIL_USE_TLS = True
class DevelopmentConfig(Config):
DEBUG = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///[Link]'
class TestingConfig(Config):
TESTING = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
WTF_CSRF_ENABLED = False
class ProductionConfig(Config):
DEBUG = False
SQLALCHEMY_DATABASE_URI = [Link]('DATABASE_URL')
config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig,
'default': DevelopmentConfig,
}

15.2 Environment Variables (.env)


pip install python-dotenv
# .env file (never commit to git!)
SECRET_KEY=mysupersecretkey
DATABASE_URL=postgresql://user:pass@localhost/mydb
MAIL_USERNAME=myemail@[Link]
MAIL_PASSWORD=apppassword
# [Link] or create_app()
from dotenv import load_dotenv
load_dotenv() # loads .env automatically
import os
secret = [Link]('SECRET_KEY')
■■ Warning: Add .env to your .gitignore — never commit secrets to version control!

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 31

Chapter 16: Flask-Mail — Sending Emails


pip install flask-mail
from flask_mail import Mail, Message
[Link]['MAIL_SERVER'] = '[Link]'
[Link]['MAIL_PORT'] = 587
[Link]['MAIL_USE_TLS'] = True
[Link]['MAIL_USERNAME'] = [Link]('MAIL_USERNAME')
[Link]['MAIL_PASSWORD'] = [Link]('MAIL_PASSWORD')
mail = Mail(app)
# Send a plain text email
@[Link]('/send')
def send_email():
msg = Message('Hello from Flask!',
sender='you@[Link]',
recipients=['friend@[Link]'])
[Link] = 'This is a test email from Flask-Mail.'
[Link](msg)
return 'Email sent!'
# Send HTML email
[Link] = render_template('email/[Link]', user=user)
# Send async (using threading)
from threading import Thread
def send_async(app, msg):
with app.app_context():
[Link](msg)
Thread(target=send_async, args=(app, msg)).start()

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 32

Chapter 17: Flask-Caching


pip install flask-caching
from flask_caching import Cache
[Link]['CACHE_TYPE'] = 'SimpleCache' # in-memory
# For Redis: 'RedisCache', CACHE_REDIS_URL = 'redis://localhost:6379/0'
[Link]['CACHE_DEFAULT_TIMEOUT'] = 300 # 5 minutes
cache = Cache(app)
# Cache a view function for 60 seconds
@[Link]('/expensive')
@[Link](timeout=60)
def expensive_view():
data = compute_something_slow()
return jsonify(data)
# Cache with dynamic key
@[Link]('/user/<int:uid>')
@[Link](timeout=120, key_prefix='user_%s')
def user_profile(uid):
user = [Link](uid)
return jsonify(user.to_dict())
# Memoize — caches by arguments
@[Link](timeout=300)
def get_data(param1, param2):
return expensive_query(param1, param2)
# Manually clear cache
[Link]('expensive_view')
[Link]()

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 33

Chapter 18: Testing Flask Applications


18.1 Test Setup
# tests/[Link]
import pytest
from app import create_app, db
@[Link]
def app():
app = create_app('[Link]')
with app.app_context():
db.create_all()
yield app
db.drop_all()
@[Link]
def client(app):
return app.test_client()
@[Link]
def runner(app):
return app.test_cli_runner()

18.2 Writing Tests


# tests/test_routes.py
def test_index(client):
response = [Link]('/')
assert response.status_code == 200
assert b'Welcome' in [Link]
def test_register(client):
response = [Link]('/auth/register', data={
'username': 'testuser',
'email': 'test@[Link]',
'password': 'password123',
'confirm': 'password123',
}, follow_redirects=True)
assert response.status_code == 200
assert b'Account created' in [Link]
def test_api_get_users(client):
resp = [Link]('/api/users')
assert resp.status_code == 200
data = resp.get_json()
assert isinstance(data, list)
def test_api_create_user(client):
resp = [Link]('/api/users',
json={'name': 'Alice', 'email': 'a@[Link]'})
assert resp.status_code == 201
assert resp.get_json()['name'] == 'Alice'
# Run with: pytest -v

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 34

Chapter 19: Deployment


19.1 Gunicorn (Production WSGI Server)
Flask's built-in server is not suitable for production. Use Gunicorn (Linux/macOS) or Waitress (Windows) as the
WSGI server.
pip install gunicorn
# Run with Gunicorn
gunicorn 'app:create_app()' --workers 4 --bind [Link]:8000
# [Link]
workers = 4
bind = '[Link]:8000'
timeout = 120
accesslog = 'logs/[Link]'
errorlog = 'logs/[Link]'
loglevel = 'warning'

19.2 Nginx as Reverse Proxy


# /etc/nginx/sites-available/myapp
server {
listen 80;
server_name [Link];
location / {
proxy_pass [Link]
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /static {
alias /var/www/myapp/static;
expires 30d;
}
}

19.3 Dockerfile
# Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY [Link] .
RUN pip install --no-cache-dir -r [Link]
COPY . .
ENV FLASK_APP=[Link]
ENV FLASK_ENV=production
EXPOSE 8000
CMD ["gunicorn", "--workers", "4", "--bind", "[Link]:8000", "run:app"]
# Build and run

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 35

docker build -t myflaskapp .


docker run -p 8000:8000 myflaskapp

19.4 Checklist for Production


• Set DEBUG=False and use environment variables for all secrets
• Use a strong, random SECRET_KEY ([Link](32).hex())
• Use HTTPS — configure SSL via Let's Encrypt with Certbot
• Use a production database (PostgreSQL recommended)
• Set up database backups
• Configure logging to file with rotation
• Set appropriate rate limiting (Flask-Limiter)
• Use a CDN for static assets in large-scale apps
• Set SQLALCHEMY_POOL_SIZE and SQLALCHEMY_MAX_OVERFLOW

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 36

Chapter 20: Advanced Patterns & Best Practices


20.1 Flask CLI Commands
import click
@[Link]('create-admin')
@[Link]('email')
@click.password_option()
def create_admin(email, password):
'''Create an admin user.'''
user = User(email=email, role='admin')
user.set_password(password)
[Link](user)
[Link]()
[Link](f'Admin {email} created.')
# Run with:
# flask create-admin admin@[Link]

20.2 Signals (Blinker)


from flask import signals
from blinker import Namespace
_signals = Namespace()
user_registered = _signals.signal('user-registered')
# Emit signal after registration
@[Link]('/register', methods=['POST'])
def register():
user = create_user([Link])
user_registered.send(app, user=user) # fire signal
return jsonify(user.to_dict()), 201
# Subscribe to signal (e.g., send welcome email)
@user_registered.connect_via(app)
def on_user_registered(sender, user, **kwargs):
send_welcome_email([Link])

20.3 Rate Limiting


pip install flask-limiter
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
limiter = Limiter(
app=app,
key_func=get_remote_address,
default_limits=['200 per day', '50 per hour']
)
@[Link]('/login', methods=['POST'])
@[Link]('5 per minute') # strict limit for login
def login(): pass
@[Link]('/api/data')

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 37

@[Link] # no limit for this endpoint


def public_data(): pass

20.4 JWT Authentication for APIs


pip install flask-jwt-extended
from flask_jwt_extended import (
JWTManager, create_access_token, jwt_required,
get_jwt_identity, create_refresh_token
)
[Link]['JWT_SECRET_KEY'] = 'jwt-secret'
jwt = JWTManager(app)
@[Link]('/api/auth/login')
def api_login():
data = request.get_json()
user = [Link].filter_by(email=data['email']).first()
if not user or not user.check_password(data['password']):
return jsonify(msg='Bad credentials'), 401
access = create_access_token(identity=[Link])
refresh = create_refresh_token(identity=[Link])
return jsonify(access_token=access, refresh_token=refresh)
@[Link]('/api/me')
@jwt_required()
def me():
uid = get_jwt_identity()
user = [Link](uid)
return jsonify(user.to_dict())

20.5 CORS for APIs


pip install flask-cors
from flask_cors import CORS
# Allow all origins (development only)
CORS(app)
# Restrict to specific origins (production)
CORS(app, resources={
r'/api/*': {
'origins': ['[Link] '[Link]
'methods': ['GET', 'POST', 'PUT', 'DELETE'],
'allow_headers': ['Content-Type', 'Authorization']
}
})

20.6 Flask Best Practices Summary


• Always use the Application Factory pattern for non-trivial apps
• Store configuration in environment variables, never in code
• Use Blueprints to organise routes by feature
• Use Flask-Migrate for all database schema changes

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 38

• Hash passwords with Werkzeug — never store plaintext


• Always use CSRF protection for forms (Flask-WTF)
• Use url_for() to build all internal links
• Write tests — use pytest with Flask's test client
• Use Gunicorn + Nginx in production, never the dev server
• Set up structured logging and monitor your app in production
• Validate and sanitise all user input
• Use HTTPS everywhere — configure with Let's Encrypt

Flask Web Framework | Basic to Advanced Python Web Development


FLASK — Complete Learning Notes Page 39

You Are Now a Flask Developer!


Topics Mastered: Introduction • Routing • Request/Response • Jinja2 Templates • Static Files • Forms •
SQLAlchemy • Authentication • Sessions • Blueprints • REST APIs • Error Handling • Hooks • Configuration • Email
• Caching • Testing • Deployment • Advanced Patterns

Build Something Amazing with Flask! ■

Flask Web Framework | Basic to Advanced Python Web Development

You might also like