1. Skip to content

1. Flask Cheat Sheet

This cheat sheet provides an exhaustive overview of the Flask micro web framework, covering essential commands, concepts, and code snippets for efficient Flask development. It aims to be a one-stop reference for common tasks and best practices.

1.1 Getting Started

1.1.1 Installation

pip install flask

Consider using a virtual environment:

python -m venv venv
source venv/bin/activate  # On Linux/macOS
venv\Scripts\activate     # On Windows

1.1.2 Flask Application Lifecycle

    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  Create App      β”‚
    β”‚  Flask(__name__) β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚
             ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  Configure App   β”‚
    β”‚  app.config[]    β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚
             ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  Register Routes β”‚
    β”‚  @app.route()    β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚
             ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  Initialize      β”‚
    β”‚  Extensions      β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚
             ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  Run Application β”‚
    β”‚  app.run()       β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

1.1.3 Basic App Structure

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

if __name__ == '__main__':
    app.run(debug=True)

1.1.4 Running the App

# Direct execution
python app.py

# Using Flask CLI
export FLASK_APP=app.py
flask run

# Run on specific host and port
flask run --host=0.0.0.0 --port=8000

1.2 Routing

1.2.1 Request Flow

    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚ HTTP Request β”‚
    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  URL Matching    β”‚
    β”‚  @app.route()    β”‚
    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  View Function   β”‚
    β”‚  Execute Logic   β”‚
    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚ Return Response  β”‚
    β”‚  HTML/JSON/etc   β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

1.2.2 Basic Route

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return 'Index Page'

@app.route('/about')
def about():
    return 'About Page'

if __name__ == '__main__':
    app.run(debug=True)

1.2.3 Dynamic Routes

from flask import Flask

app = Flask(__name__)

# String parameter (default)
@app.route('/user/<username>')
def show_user_profile(username):
    return f'User: {username}'

# Integer parameter
@app.route('/post/<int:post_id>')
def show_post(post_id):
    return f'Post {post_id}'

# Float parameter
@app.route('/price/<float:amount>')
def show_price(amount):
    return f'Price: ${amount:.2f}'

# Path parameter (accepts slashes)
@app.route('/path/<path:subpath>')
def show_path(subpath):
    return f'Path: {subpath}'

# UUID parameter
@app.route('/uuid/<uuid:id>')
def show_uuid(id):
    return f'UUID: {id}'

if __name__ == '__main__':
    app.run(debug=True)

1.2.4 HTTP Methods

    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚   GET    β”‚     β”‚   POST   β”‚     β”‚  DELETE  β”‚
    β”‚ Retrieve β”‚     β”‚  Create  β”‚     β”‚  Remove  β”‚
    β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
         β”‚                β”‚                 β”‚
         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                          ↓
                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                 β”‚  Flask Route   β”‚
                 β”‚  View Function β”‚
                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
from flask import Flask, request, jsonify

app = Flask(__name__)

# Multiple methods on one route
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        return f"Logging in: {username}"
    return "Show login form"

# RESTful API example
@app.route('/api/users/<int:user_id>', methods=['GET', 'PUT', 'DELETE'])
def user_api(user_id):
    if request.method == 'GET':
        return jsonify({'user_id': user_id, 'action': 'fetch'})
    elif request.method == 'PUT':
        data = request.get_json()
        return jsonify({'user_id': user_id, 'action': 'update', 'data': data})
    elif request.method == 'DELETE':
        return jsonify({'user_id': user_id, 'action': 'delete'})

# Separate routes for different methods
@app.route('/resource', methods=['GET'])
def get_resource():
    return "GET resource"

@app.route('/resource', methods=['POST'])
def create_resource():
    return "POST resource"

if __name__ == '__main__':
    app.run(debug=True)

1.2.5 URL Building

from flask import Flask, url_for, redirect

app = Flask(__name__)

@app.route('/')
def index():
    return 'Index'

@app.route('/login')
def login():
    return 'Login'

@app.route('/user/<username>')
def profile(username):
    return f'{username}\'s profile'

@app.route('/admin')
def admin():
    # Redirect to login
    return redirect(url_for('login'))

@app.route('/user-redirect/<username>')
def user_redirect(username):
    # Redirect to user profile
    return redirect(url_for('profile', username=username))

# Generate URLs programmatically
with app.test_request_context():
    print(url_for('index'))                              # Output: /
    print(url_for('login'))                              # Output: /login
    print(url_for('login', next='/'))                    # Output: /login?next=%2F
    print(url_for('profile', username='John Doe'))       # Output: /user/John%20Doe
    print(url_for('index', _external=True))              # Output: http://localhost/
    print(url_for('static', filename='style.css'))       # Output: /static/style.css

if __name__ == '__main__':
    app.run(debug=True)

1.3 Templates

1.3.1 Template Rendering Flow

    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  View Function β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
            β”‚
            ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚ render_templateβ”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
            β”‚
            ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  Jinja2 Engine β”‚
    β”‚  Process Template
    β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
            β”‚
            ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  HTML Response β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

1.3.2 Basic Template Rendering

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
    return render_template('hello.html', name=name)

@app.route('/users')
def users():
    users_list = [
        {'id': 1, 'name': 'Alice', 'role': 'Admin'},
        {'id': 2, 'name': 'Bob', 'role': 'User'},
        {'id': 3, 'name': 'Charlie', 'role': 'User'}
    ]
    return render_template('users.html', users=users_list)

@app.route('/dashboard')
def dashboard():
    context = {
        'title': 'Dashboard',
        'user': {'name': 'John Doe', 'email': 'john@example.com'},
        'stats': {'views': 1500, 'posts': 42, 'comments': 128}
    }
    return render_template('dashboard.html', **context)

if __name__ == '__main__':
    app.run(debug=True)

1.3.3 Template (templates/hello.html)

<!DOCTYPE html>
<html>
<head>
    <title>Hello</title>
</head>
<body>
    {% if name %}
        <h1>Hello {{ name }}!</h1>
    {% else %}
        <h1>Hello, World!</h1>
    {% endif %}
</body>
</html>

1.3.4 Template Inheritance

Base template (templates/base.html):

<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}My Website{% endblock %}</title>
</head>
<body>
    <header>
        <h1>{% block header %}My Website{% endblock %}</h1>
    </header>

    <main>
        {% block content %}{% endblock %}
    </main>

    <footer>
        <p>&copy; 2025 My Website</p>
    </footer>
</body>
</html>

Child template (templates/hello.html):

{% extends "base.html" %}

{% block title %}Hello{% endblock %}

{% block content %}
    {% if name %}
        <h1>Hello {{ name }}!</h1>
    {% else %}
        <h1>Hello, World!</h1>
    {% endif %}
{% endblock %}

1.3.5 Jinja2 Template Engine

# Common Jinja2 Syntax:
# {{ variable }}                    - Output variable
# {% tag %}                         - Logic tag (if, for, etc.)
# {{ variable|filter }}             - Apply filter
# {# comment #}                     - Comment (not rendered)
# {% extends "base.html" %}         - Template inheritance
# {% block name %}{% endblock %}    - Define block
# {% include "partial.html" %}      - Include template
# {{ url_for('view_name') }}        - Generate URL

Example template (templates/demo.html):

<!DOCTYPE html>
<html>
<head>
    <title>{{ title|default('Default Title') }}</title>
</head>
<body>
    {# This is a comment #}

    {# Variables #}
    <h1>Hello {{ name|capitalize }}!</h1>

    {# Conditionals #}
    {% if user.is_admin %}
        <p>Welcome, Admin!</p>
    {% elif user.is_authenticated %}
        <p>Welcome, {{ user.name }}!</p>
    {% else %}
        <p>Please log in.</p>
    {% endif %}

    {# Loops #}
    <ul>
    {% for item in items %}
        <li>{{ loop.index }}. {{ item.name }} - ${{ item.price|round(2) }}</li>
    {% else %}
        <li>No items available</li>
    {% endfor %}
    </ul>

    {# URL generation #}
    <a href="{{ url_for('index') }}">Home</a>
    <a href="{{ url_for('profile', username='john') }}">Profile</a>
</body>
</html>

1.3.6 Common Template Filters

# String filters
{{ 'hello world'|capitalize }}        # 'Hello world'
{{ 'HELLO'|lower }}                   # 'hello'
{{ 'hello'|upper }}                   # 'HELLO'
{{ 'hello world'|title }}             # 'Hello World'
{{ '  text  '|trim }}                 # 'text'
{{ 'Hello <b>World</b>'|striptags }}  # 'Hello World'

# Numeric filters
{{ 42.5678|round }}                   # 43.0
{{ 42.5678|round(2) }}                # 42.57
{{ 42|abs }}                          # 42
{{ -42|abs }}                         # 42

# List filters
{{ [1, 2, 3]|length }}                # 3
{{ [3, 1, 2]|sort }}                  # [1, 2, 3]
{{ [1, 2, 3]|reverse }}               # [3, 2, 1]
{{ [1, 2, 3]|first }}                 # 1
{{ [1, 2, 3]|last }}                  # 3
{{ ['a', 'b', 'c']|join(', ') }}      # 'a, b, c'

# Conditional filters
{{ variable|default('N/A') }}         # Use default if undefined
{{ html_content|safe }}               # Mark as safe HTML
{{ none_value|default('Empty', true) }}  # Use default if falsy

# Date/Time filters (requires datetime object)
{{ date_obj|strftime('%Y-%m-%d') }}   # Format datetime

# Custom example
{{ 'hello {0}'|format('world') }}     # 'hello world'
{{ 'old text'|replace('old', 'new') }}  # 'new text'

1.4 Forms

1.4.1 Form Handling Flow

    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  GET Request β”‚
    β”‚  Show Form   β”‚
    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚ User Fills   β”‚
    β”‚ Form & Submitβ”‚
    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚ POST Request β”‚
    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  Validation  β”‚
    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
      β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”
      β”‚         β”‚
   (Pass)    (Fail)
      β”‚         β”‚
      ↓         ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Process  β”‚ β”‚  Show    β”‚
β”‚ & Redirectβ”‚ β”‚  Errors  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

1.4.2 Basic Form Handling

from flask import Flask, request, render_template, 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
        print(f"Name: {name}, Email: {email}, Message: {message}")

        return redirect(url_for('success'))

    return render_template('contact.html')

@app.route('/success')
def success():
    return "Form submitted successfully!"

if __name__ == '__main__':
    app.run(debug=True)

HTML template (templates/contact.html):

<!DOCTYPE html>
<html>
<head>
    <title>Contact Form</title>
</head>
<body>
    <h1>Contact Us</h1>
    <form method="post">
        <label for="name">Name:</label><br>
        <input type="text" id="name" name="name" required><br><br>

        <label for="email">Email:</label><br>
        <input type="email" id="email" name="email" required><br><br>

        <label for="message">Message:</label><br>
        <textarea id="message" name="message" rows="5" cols="40" required></textarea><br><br>

        <input type="submit" value="Submit">
    </form>
</body>
</html>

1.4.3 Using Flask-WTF

Installation:

pip install flask-wtf

Configuration:

import os
from flask import Flask
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, TextAreaField
from wtforms.validators import DataRequired, Email, Length, EqualTo

app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev-secret-key-change-in-production')

Flask-WTF Flow:

    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚ Define Form  β”‚
    β”‚ Class (WTF)  β”‚
    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚ Instantiate  β”‚
    β”‚ in View      β”‚
    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚ Render in    β”‚
    β”‚ Template     β”‚
    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚ Validate on  β”‚
    β”‚ Submit       β”‚
    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
      β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”
      β”‚         β”‚
   (Valid)  (Invalid)
      β”‚         β”‚
      ↓         ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Process  β”‚ β”‚  Show    β”‚
β”‚ Data     β”‚ β”‚  Errors  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

1.4.4 Define a Form (forms.py)

from flask_wtf import FlaskForm
from wtforms import (
    StringField, 
    PasswordField, 
    SubmitField, 
    TextAreaField, 
    BooleanField,
    SelectField,
    RadioField,
    DateField
)
from wtforms.validators import (
    DataRequired, 
    Length, 
    Email, 
    EqualTo, 
    ValidationError,
    Regexp
)

class ContactForm(FlaskForm):
    name = StringField('Name', validators=[
        DataRequired(message='Name is required'),
        Length(min=2, max=50, message='Name must be between 2 and 50 characters')
    ])
    email = StringField('Email', validators=[
        DataRequired(message='Email is required'),
        Email(message='Invalid email address')
    ])
    message = TextAreaField('Message', validators=[
        DataRequired(message='Message is required'),
        Length(min=10, max=500, message='Message must be between 10 and 500 characters')
    ])
    submit = SubmitField('Send Message')

class RegistrationForm(FlaskForm):
    username = StringField('Username', validators=[
        DataRequired(),
        Length(min=4, max=20),
        Regexp('^[A-Za-z0-9_]+$', message='Username must contain only letters, numbers, and underscores')
    ])
    email = StringField('Email', validators=[
        DataRequired(),
        Email()
    ])
    password = PasswordField('Password', validators=[
        DataRequired(),
        Length(min=8, message='Password must be at least 8 characters')
    ])
    confirm_password = PasswordField('Confirm Password', validators=[
        DataRequired(),
        EqualTo('password', message='Passwords must match')
    ])
    agree = BooleanField('I agree to the Terms and Conditions', validators=[
        DataRequired(message='You must agree to the terms')
    ])
    submit = SubmitField('Sign Up')

class ProfileForm(FlaskForm):
    bio = TextAreaField('Bio', validators=[Length(max=500)])
    country = SelectField('Country', choices=[
        ('', 'Select Country'),
        ('us', 'United States'),
        ('uk', 'United Kingdom'),
        ('ca', 'Canada')
    ])
    gender = RadioField('Gender', choices=[
        ('male', 'Male'),
        ('female', 'Female'),
        ('other', 'Other')
    ])
    birthdate = DateField('Birthdate', format='%Y-%m-%d')
    submit = SubmitField('Update Profile')

1.4.5 Render a Form in a Template

<form method="post">
    {{ form.csrf_token }}
    <div class="form-group">
        {{ form.name.label }}<br>
        {{ form.name(class="form-control") }}
        {% if form.name.errors %}
            <ul class="errors">
                {% for error in form.name.errors %}
                    <li>{{ error }}</li>
                {% endfor %}
            </ul>
        {% endif %}
    </div>
    <div class="form-group">
        {{ form.email.label }}<br>
        {{ form.email(class="form-control") }}
        {% if form.email.errors %}
            <ul class="errors">
                {% for error in form.email.errors %}
                    <li>{{ error }}</li>
                {% endfor %}
            </ul>
        {% endif %}
    </div>
    <div class="form-group">
        {{ form.message.label }}<br>
        {{ form.message(class="form-control") }}
        {% if form.message.errors %}
            <ul class="errors">
                {% for error in form.message.errors %}
                    <li>{{ error }}</li>
                {% endfor %}
            </ul>
        {% endif %}
    </div>
    <div class="form-group">
        {{ form.agree.label }}
        {{ form.agree }}
        {% if form.agree.errors %}
            <ul class="errors">
                {% for error in form.agree.errors %}
                    <li>{{ error }}</li>
                {% endfor %}
            </ul>
        {% endif %}
    </div>
    {{ form.submit(class="btn btn-primary") }}
</form>

1.4.6 Process Form Data in a View

from flask import Flask, render_template, request, redirect, url_for
from forms import MyForm

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'

@app.route('/form', methods=['GET', 'POST'])
def my_form_view():
    form = MyForm()
    if form.validate_on_submit():
        name = form.name.data
        email = form.email.data
        message = form.message.data
        # Process the data (e.g., save to database, send email)
        return redirect(url_for('success'))
    return render_template('myform.html', form=form)

@app.route('/success')
def success():
    return "Form submitted successfully!"

if __name__ == '__main__':
    app.run(debug=True)

1.5 Databases

1.5.1 Database Integration Flow

    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  Configure DB    β”‚
    β”‚  SQLAlchemy      β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚
             ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  Define Models   β”‚
    β”‚  db.Model        β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚
             ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  Create Tables   β”‚
    β”‚  db.create_all() β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚
             ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  CRUD Operations β”‚
    β”‚  Query, Add, etc β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

1.5.2 Using Flask-SQLAlchemy

Installation:

pip install flask-sqlalchemy

Configuration:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import os

app = Flask(__name__)

# Database configuration
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', 'sqlite:///app.db')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_ECHO'] = False  # Set to True to log SQL queries

db = SQLAlchemy(app)

Database URI examples:

# SQLite (local file)
'sqlite:///database.db'
'sqlite:////absolute/path/to/database.db'

# PostgreSQL
'postgresql://username:password@localhost:5432/database_name'

# MySQL
'mysql://username:password@localhost:3306/database_name'

# MySQL with PyMySQL
'mysql+pymysql://username:password@localhost:3306/database_name'

1.5.3 Define Models

from flask_sqlalchemy import SQLAlchemy
from datetime import datetime

db = SQLAlchemy()

class User(db.Model):
    __tablename__ = 'users'

    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False, index=True)
    email = db.Column(db.String(120), unique=True, nullable=False, index=True)
    password_hash = db.Column(db.String(128), nullable=False)
    is_active = db.Column(db.Boolean, default=True)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

    # Relationships
    posts = db.relationship('Post', backref='author', lazy='dynamic', cascade='all, delete-orphan')
    profile = db.relationship('Profile', backref='user', uselist=False, cascade='all, delete-orphan')

    def __repr__(self):
        return f"<User {self.username}>"

class Post(db.Model):
    __tablename__ = 'posts'

    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(200), nullable=False)
    slug = db.Column(db.String(200), unique=True, nullable=False, index=True)
    content = db.Column(db.Text, nullable=False)
    published = db.Column(db.Boolean, default=False)
    views = db.Column(db.Integer, default=0)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

    # Foreign key
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)

    # Many-to-many relationship
    tags = db.relationship('Tag', secondary='post_tags', backref=db.backref('posts', lazy='dynamic'))

    def __repr__(self):
        return f"<Post {self.title}>"

class Profile(db.Model):
    __tablename__ = 'profiles'

    id = db.Column(db.Integer, primary_key=True)
    bio = db.Column(db.Text)
    avatar = db.Column(db.String(200), default='default.jpg')
    website = db.Column(db.String(200))
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)

    def __repr__(self):
        return f"<Profile for User {self.user_id}>"

class Tag(db.Model):
    __tablename__ = 'tags'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), unique=True, nullable=False)

    def __repr__(self):
        return f"<Tag {self.name}>"

# Association table for many-to-many relationship
post_tags = db.Table('post_tags',
    db.Column('post_id', db.Integer, db.ForeignKey('posts.id'), primary_key=True),
    db.Column('tag_id', db.Integer, db.ForeignKey('tags.id'), primary_key=True),
    db.Column('created_at', db.DateTime, default=datetime.utcnow)
)

1.5.4 Create and Manage Tables

from yourapp import app, db

# Create all tables
with app.app_context():
    db.create_all()

# Drop all tables (careful!)
with app.app_context():
    db.drop_all()

# Recreate all tables (drop and create)
with app.app_context():
    db.drop_all()
    db.create_all()

# Add sample data
with app.app_context():
    # Create user
    user = User(username='john_doe', email='john@example.com', password_hash='hashed_password')
    db.session.add(user)
    db.session.commit()

    # Create post for user
    post = Post(title='First Post', slug='first-post', content='Hello World!', user_id=user.id)
    db.session.add(post)
    db.session.commit()

    # Create multiple objects
    users = [
        User(username='alice', email='alice@example.com', password_hash='hash1'),
        User(username='bob', email='bob@example.com', password_hash='hash2')
    ]
    db.session.add_all(users)
    db.session.commit()

1.5.5 Querying the Database

from yourapp import db, User, Post, Tag
from sqlalchemy import and_, or_, not_

# Basic queries
all_users = User.query.all()                              # Get all users
first_user = User.query.first()                           # Get first user
user = User.query.get(1)                                  # Get by primary key
user = User.query.get_or_404(1)                           # Get or return 404

# Filtering
user = User.query.filter_by(username='john_doe').first()  # Simple filter
users = User.query.filter(User.username == 'john').all()  # Complex filter
active_users = User.query.filter_by(is_active=True).all()

# Multiple conditions
users = User.query.filter(
    and_(
        User.is_active == True,
        User.created_at > datetime(2024, 1, 1)
    )
).all()

users = User.query.filter(
    or_(
        User.username == 'john',
        User.username == 'alice'
    )
).all()

# Ordering
users = User.query.order_by(User.created_at.desc()).all()
users = User.query.order_by(User.username.asc()).all()

# Limiting
users = User.query.limit(10).all()
users = User.query.offset(5).limit(10).all()

# Pagination
page = 1
per_page = 20
pagination = User.query.paginate(page=page, per_page=per_page, error_out=False)
users = pagination.items
total = pagination.total

# Counting
user_count = User.query.count()
active_count = User.query.filter_by(is_active=True).count()

# Like queries (pattern matching)
users = User.query.filter(User.username.like('%john%')).all()
users = User.query.filter(User.email.ilike('%@GMAIL.COM')).all()  # Case-insensitive

# In queries
usernames = ['alice', 'bob', 'charlie']
users = User.query.filter(User.username.in_(usernames)).all()

# Relationships
user = User.query.get(1)
user_posts = user.posts.all()                             # Get user's posts
user_posts = user.posts.filter_by(published=True).all()   # Filter related posts

post = Post.query.get(1)
author = post.author                                      # Get post's author

# Joins
results = db.session.query(User, Post).join(Post).all()
results = User.query.join(Post).filter(Post.published == True).all()

# Eager loading (avoid N+1 queries)
from sqlalchemy.orm import joinedload
users = User.query.options(joinedload(User.posts)).all()

# CREATE
new_user = User(username='jane_doe', email='jane@example.com', password_hash='hash')
db.session.add(new_user)
db.session.commit()

# UPDATE
user = User.query.get(1)
user.email = 'newemail@example.com'
db.session.commit()

# Or update multiple fields
User.query.filter_by(id=1).update({'email': 'updated@example.com', 'is_active': True})
db.session.commit()

# DELETE
user = User.query.get(1)
db.session.delete(user)
db.session.commit()

# Or delete with filter
User.query.filter_by(is_active=False).delete()
db.session.commit()

# Bulk operations
users = User.query.filter(User.is_active == False).all()
for user in users:
    db.session.delete(user)
db.session.commit()

# Rollback on error
try:
    user = User(username='test', email='test@example.com', password_hash='hash')
    db.session.add(user)
    db.session.commit()
except Exception as e:
    db.session.rollback()
    print(f"Error: {e}")

1.6 Static Files

1.6.1 Static File Structure

your_app/
    β”œβ”€β”€ app.py
    β”œβ”€β”€ templates/
    β”‚   └── index.html
    └── static/
        β”œβ”€β”€ css/
        β”‚   └── style.css
        β”œβ”€β”€ js/
        β”‚   └── script.js
        └── images/
            └── logo.png

1.6.2 Configure Static Files

from flask import Flask, url_for

app = Flask(__name__, 
    static_folder='static',      # Default: 'static'
    static_url_path='/static'    # Default: '/static'
)

# Custom static folder
app = Flask(__name__, 
    static_folder='assets',
    static_url_path='/assets'
)

# Generate static file URLs
with app.test_request_context():
    print(url_for('static', filename='css/style.css'))
    # Output: /static/css/style.css

1.6.3 Use in Templates

<!DOCTYPE html>
<html>
<head>
    <title>My App</title>
    <!-- CSS -->
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
    <link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
</head>
<body>
    <!-- Image -->
    <img src="{{ url_for('static', filename='images/logo.png') }}" alt="Logo">

    <!-- JavaScript -->
    <script src="{{ url_for('static', filename='js/script.js') }}"></script>
    <script src="{{ url_for('static', filename='js/jquery.min.js') }}"></script>

    <!-- Favicon -->
    <link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
</body>
</html>

1.6.4 Serving Static Files in Production

# In development, Flask serves static files automatically
# In production, use a web server like Nginx

# Nginx configuration example
"""
location /static {
    alias /path/to/your/app/static;
    expires 30d;
    add_header Cache-Control "public, immutable";
}
"""

1.7 Blueprints

1.7.1 Blueprint Architecture

    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚   Flask App     β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚
      β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”
      β”‚             β”‚
      ↓             ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Blueprint β”‚ β”‚ Blueprint β”‚
β”‚   Auth    β”‚ β”‚   Blog    β”‚
β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
      β”‚             β”‚
      ↓             ↓
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”
  β”‚Routes β”‚     β”‚Routes β”‚
  β”‚Views  β”‚     β”‚Views  β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”˜

1.7.2 Application Structure with Blueprints

your_app/
    β”œβ”€β”€ app.py
    β”œβ”€β”€ config.py
    β”œβ”€β”€ requirements.txt
    β”œβ”€β”€ blueprints/
    β”‚   β”œβ”€β”€ __init__.py
    β”‚   β”œβ”€β”€ auth/
    β”‚   β”‚   β”œβ”€β”€ __init__.py
    β”‚   β”‚   β”œβ”€β”€ routes.py
    β”‚   β”‚   β”œβ”€β”€ forms.py
    β”‚   β”‚   └── templates/
    β”‚   β”‚       └── auth/
    β”‚   β”‚           β”œβ”€β”€ login.html
    β”‚   β”‚           └── register.html
    β”‚   └── blog/
    β”‚       β”œβ”€β”€ __init__.py
    β”‚       β”œβ”€β”€ routes.py
    β”‚       β”œβ”€β”€ models.py
    β”‚       └── templates/
    β”‚           └── blog/
    β”‚               β”œβ”€β”€ index.html
    β”‚               └── post.html
    β”œβ”€β”€ static/
    └── templates/
        └── base.html

1.7.3 Create a Blueprint

# blueprints/auth/__init__.py
from flask import Blueprint

auth_bp = Blueprint(
    'auth',
    __name__,
    url_prefix='/auth',                    # All routes prefixed with /auth
    template_folder='templates',            # Blueprint-specific templates
    static_folder='static'                  # Blueprint-specific static files
)

from . import routes

# blueprints/auth/routes.py
from flask import render_template, redirect, url_for, flash, request
from . import auth_bp

@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        # Handle login
        return redirect(url_for('auth.dashboard'))
    return render_template('auth/login.html')

@auth_bp.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        # Handle registration
        return redirect(url_for('auth.login'))
    return render_template('auth/register.html')

@auth_bp.route('/logout')
def logout():
    # Handle logout
    return redirect(url_for('main.index'))

@auth_bp.route('/dashboard')
def dashboard():
    return render_template('auth/dashboard.html')

1.7.4 Register Blueprints

# app.py
from flask import Flask
from blueprints.auth import auth_bp
from blueprints.blog import blog_bp

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'

# Register blueprints
app.register_blueprint(auth_bp)              # Prefix: /auth
app.register_blueprint(blog_bp)              # Prefix: /blog

# Register with custom URL prefix
app.register_blueprint(auth_bp, url_prefix='/authentication')

# Register at root level
app.register_blueprint(blog_bp, url_prefix='/')

@app.route('/')
def index():
    return "Main Index"

if __name__ == '__main__':
    app.run(debug=True)

1.7.5 Blueprint with Error Handlers

# blueprints/blog/__init__.py
from flask import Blueprint, render_template

blog_bp = Blueprint('blog', __name__, url_prefix='/blog')

@blog_bp.errorhandler(404)
def blog_not_found(e):
    return render_template('blog/404.html'), 404

@blog_bp.errorhandler(500)
def blog_error(e):
    return render_template('blog/500.html'), 500

from . import routes

1.7.6 Blueprint with Before/After Request Hooks

# blueprints/auth/routes.py
from flask import g, session
from . import auth_bp

@auth_bp.before_request
def before_request():
    """Runs before each request to this blueprint"""
    g.user = session.get('user')

@auth_bp.after_request
def after_request(response):
    """Runs after each request to this blueprint"""
    response.headers['X-Blueprint'] = 'auth'
    return response

@auth_bp.teardown_request
def teardown_request(exception):
    """Runs after each request, even if there's an exception"""
    if exception:
        # Log the exception
        print(f"Exception: {exception}")

1.7.7 URL Generation with Blueprints

from flask import url_for

# Within the same blueprint
url_for('login')                    # Relative to current blueprint
url_for('.login')                   # Explicitly relative

# From different blueprint
url_for('auth.login')               # Fully qualified
url_for('blog.index')               # Different blueprint

# With parameters
url_for('blog.post', post_id=123)   # /blog/post/123

# External URLs
url_for('auth.login', _external=True)  # http://example.com/auth/login

1.8 Flask Extensions

1.8.1 Flask-Mail

Installation:

pip install flask-mail

Configuration:

from flask import Flask
from flask_mail import Mail, Message
import os

app = Flask(__name__)

# Email configuration
app.config['MAIL_SERVER'] = os.environ.get('MAIL_SERVER', 'smtp.gmail.com')
app.config['MAIL_PORT'] = int(os.environ.get('MAIL_PORT', 587))
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USE_SSL'] = False
app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME')
app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD')
app.config['MAIL_DEFAULT_SENDER'] = os.environ.get('MAIL_DEFAULT_SENDER')

mail = Mail(app)

Sending Emails:

from flask import render_template
from flask_mail import Message
from threading import Thread

# Simple text email
@app.route('/send-simple')
def send_simple_email():
    msg = Message(
        subject="Test Email",
        sender="noreply@example.com",
        recipients=["user@example.com"]
    )
    msg.body = "This is a plain text email body"
    mail.send(msg)
    return "Email sent!"

# HTML email
@app.route('/send-html')
def send_html_email():
    msg = Message(
        subject="Welcome!",
        recipients=["user@example.com"]
    )
    msg.body = "This is the plain text fallback"
    msg.html = render_template('email/welcome.html', username='John')
    mail.send(msg)
    return "HTML email sent!"

# Email with attachment
@app.route('/send-attachment')
def send_attachment_email():
    msg = Message(
        subject="Document Attached",
        recipients=["user@example.com"]
    )
    msg.body = "Please find the attached document."

    with app.open_resource("document.pdf") as fp:
        msg.attach("document.pdf", "application/pdf", fp.read())

    mail.send(msg)
    return "Email with attachment sent!"

# Async email sending
def send_async_email(app, msg):
    with app.app_context():
        mail.send(msg)

@app.route('/send-async')
def send_async():
    msg = Message(
        subject="Async Email",
        recipients=["user@example.com"],
        body="This email is sent asynchronously"
    )
    Thread(target=send_async_email, args=(app, msg)).start()
    return "Email is being sent in background!"

# Bulk emails
@app.route('/send-bulk')
def send_bulk_emails():
    users = [
        {'email': 'user1@example.com', 'name': 'Alice'},
        {'email': 'user2@example.com', 'name': 'Bob'}
    ]

    with mail.connect() as conn:
        for user in users:
            msg = Message(
                subject="Bulk Email",
                recipients=[user['email']],
                body=f"Hello {user['name']}!"
            )
            conn.send(msg)

    return "Bulk emails sent!"

1.8.2 Flask-Migrate

Installation:

pip install flask-migrate

Configuration:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
db = SQLAlchemy(app)
migrate = Migrate(app, db)

Migration Commands:

flask db init  # Initialize the migration repository
flask db migrate -m "Initial migration"  # Create a new migration
flask db upgrade  # Apply the latest migration
flask db downgrade  # Revert to a previous migration

1.8.3 Flask-Login

Installation:

pip install flask-login

Authentication Flow:

    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚ User Login   β”‚
    β”‚ Submit Form  β”‚
    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
           ↓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  Validate    β”‚
    β”‚  Credentials β”‚
    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
           β”‚
      β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”
      β”‚         β”‚
   (Valid)  (Invalid)
      β”‚         β”‚
      ↓         ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚login_userβ”‚ β”‚  Show    β”‚
β”‚Session   β”‚ β”‚  Error   β”‚
β”‚Created   β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
     β”‚
     ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Access      β”‚
β”‚  Protected   β”‚
β”‚  Routes      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Configuration:

from flask import Flask
from flask_login import LoginManager

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-change-in-production'

# Initialize Flask-Login
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'auth.login'          # Redirect unauthorized users
login_manager.login_message = 'Please log in to access this page.'
login_manager.login_message_category = 'info'

User Model:

from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash

class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password_hash = db.Column(db.String(128))
    is_active = db.Column(db.Boolean, default=True)

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

    # UserMixin provides these methods:
    # - is_authenticated
    # - is_active
    # - is_anonymous
    # - get_id()

User Loader:

from flask_login import login_manager

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

# Optional: handle invalid users
@login_manager.unauthorized_handler
def unauthorized():
    return "You must be logged in to access this page", 403

Login/Logout Views:

from flask import render_template, redirect, url_for, flash, request
from flask_login import login_user, logout_user, login_required, current_user

@app.route('/login', methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('dashboard'))

    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        remember = request.form.get('remember', False)

        user = User.query.filter_by(username=username).first()

        if user and user.check_password(password):
            login_user(user, remember=remember)

            # Redirect to next page or dashboard
            next_page = request.args.get('next')
            return redirect(next_page) if next_page else redirect(url_for('dashboard'))
        else:
            flash('Invalid username or password', 'danger')

    return render_template('login.html')

@app.route('/logout')
@login_required
def logout():
    logout_user()
    flash('You have been logged out', 'info')
    return redirect(url_for('index'))

@app.route('/dashboard')
@login_required
def dashboard():
    return render_template('dashboard.html', user=current_user)

# Protect specific routes
@app.route('/admin')
@login_required
def admin():
    if not current_user.is_admin:
        flash('Access denied', 'danger')
        return redirect(url_for('index'))
    return render_template('admin.html')

Using current_user:

from flask_login import current_user

@app.route('/profile')
@login_required
def profile():
    # Access current logged-in user
    username = current_user.username
    email = current_user.email
    is_authenticated = current_user.is_authenticated

    return render_template('profile.html', user=current_user)

# In templates:
# {% if current_user.is_authenticated %}
#     <p>Welcome, {{ current_user.username }}!</p>
#     <a href="{{ url_for('logout') }}">Logout</a>
# {% else %}
#     <a href="{{ url_for('login') }}">Login</a>
# {% endif %}

1.9 Testing

1.9.1 Using pytest

Installation:

pip install pytest pytest-flask

Test Example:

import pytest
from yourapp import app

@pytest.fixture
def client():
    app.config['TESTING'] = True
    with app.test_client() as client:
        yield client

def test_index_route(client):
    response = client.get('/')
    assert response.status_code == 200
    assert b'Hello, World!' in response.data

1.9.2 Using unittest

import unittest
from yourapp import app

class MyTestCase(unittest.TestCase):
    def setUp(self):
        app.config['TESTING'] = True
        self.app = app.test_client()

    def test_index_route(self):
        response = self.app.get('/')
        self.assertEqual(response.status_code, 200)
        self.assertIn(b'Hello, World!', response.data)

1.10 Deployment

1.10.1 Production Settings

  • Set FLASK_ENV=production to disable debug mode.
  • Use a production WSGI server (e.g., Gunicorn, uWSGI).
  • Configure your web server (e.g., Nginx, Apache) to proxy requests to the WSGI server.
  • Use a process manager (e.g., Supervisor, systemd) to manage the WSGI server.
  • Configure logging.
  • Use HTTPS.

1.10.2 WSGI Servers

Gunicorn:

pip install gunicorn
gunicorn yourapp:app --bind 0.0.0.0:8000

uWSGI:

pip install uwsgi
uwsgi --http 0.0.0.0:8000 --module yourapp

1.10.3 Environment Variables

Use environment variables for sensitive settings (e.g., SECRET_KEY, database credentials).

1.10.4 Example Deployment with Gunicorn and Nginx

  1. Install Gunicorn: pip install gunicorn
  2. Create a WSGI entry point: yourapp.py (already covered)
  3. Create a systemd service file: /etc/systemd/system/yourapp.service
[Unit]
Description=Gunicorn instance to serve yourapp
After=network.target

[Service]
User=youruser
Group=www-data
WorkingDirectory=/path/to/your/app
ExecStart=/path/to/your/venv/bin/gunicorn --workers 3 --max-requests 500 --bind unix:/run/yourapp.sock yourapp:app

[Install]
WantedBy=multi-user.target
  1. Create an Nginx configuration file: /etc/nginx/sites-available/yourapp
server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/yourapp.sock;
    }

    location /static {
        alias /path/to/your/app/static;
    }
}
  1. Create a symbolic link:
sudo ln -s /etc/nginx/sites-available/yourapp /etc/nginx/sites-enabled
  1. Restart Nginx:
sudo systemctl restart nginx

1.11 Security

  • Use a strong SECRET_KEY and keep it secret.
  • Use HTTPS.
  • Sanitize user input to prevent XSS attacks.
  • Use parameterized queries to prevent SQL injection.
  • Use a Content Security Policy (CSP) to prevent various attacks.
  • Protect against CSRF attacks using Flask-WTF.
  • Limit file upload sizes.
  • Validate file uploads.
  • Use a security linter (e.g., Bandit).

1.12 Logging

1.12.1 Configure Logging

import logging

logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# In your code:
logger.info('This is an info message')
logger.warning('This is a warning message')
logger.error('This is an error message')

1.12.2 Logging to a File

import logging
import logging.handlers

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# Create a file handler
log_handler = logging.handlers.RotatingFileHandler('yourapp.log', maxBytes=10240, backupCount=5)
log_handler.setLevel(logging.DEBUG)

# Create a logging format
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
log_handler.setFormatter(formatter)

# Add the handlers to the logger
logger.addHandler(log_handler)

1.13 Flask CLI

Flask provides a command-line interface for managing your application.

  • flask run: Runs the development server.
  • flask shell: Opens a Python shell with the Flask application context.
  • flask routes: Shows the registered routes.
  • flask db: Manages database migrations (requires Flask-Migrate).

1.14 Context Processors

Context processors inject variables automatically into all templates.

from flask import Flask, render_template

app = Flask(__name__)

@app.context_processor
def inject_variables():
    return dict(site_name="My Awesome Website")

@app.route('/')
def index():
    return render_template('index.html')

In templates/index.html:

<h1>Welcome to {{ site_name }}!</h1>

1.15 Error Handling

1.15.1 Custom Error Pages

from flask import Flask, render_template

app = Flask(__name__)

@app.errorhandler(404)
def page_not_found(e):
    return render_template('404.html'), 404

@app.errorhandler(500)
def internal_server_error(e):
    return render_template('500.html'), 500

1.15.2 Logging Exceptions

import logging
from flask import Flask, render_template

app = Flask(__name__)
logger = logging.getLogger(__name__)

@app.route('/')
def index():
    try:
        # Some code that might raise an exception
        raise ValueError("Something went wrong")
    except Exception as e:
        logger.exception("An error occurred")
        return render_template('error.html', error=str(e)), 500

1.16 Flask-RESTful

1.16.1 Installation

pip install flask-restful

1.16.2 Define Resources

from flask import Flask
from flask_restful import Api, Resource

app = Flask(__name__)
api = Api(app)

class HelloWorld(Resource):
    def get(self):
        return {'hello': 'world'}

api.add_resource(HelloWorld, '/')

1.16.3 Request Parsing

from flask_restful import reqparse

parser = reqparse.RequestParser()
parser.add_argument('name', required=True, help="Name is required")

class MyResource(Resource):
    def post(self):
        args = parser.parse_args()
        name = args['name']
        return {'message': f'Hello, {name}!'}

1.17 Session Management

from flask import Flask, session, redirect, url_for, escape, request

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'

@app.route('/')
def index():
    if 'username' in session:
        return f'Logged in as {escape(session["username"])}
Click here to <a href="{url_for("logout")}">logout</a>'
    return 'You are not logged in
Click here to <a href="{url_for("login")}">login</a>'

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        session['username'] = request.form['username']
        return redirect(url_for('index'))
    return '''
        <form method="post">
            <p><input type=text name=username>
            <p><input type=submit value=Login>
        </form>
    '''

@app.route('/logout')
def logout():
    session.pop('username', None)
    return redirect(url_for('index'))

1.18 Flask-CORS

1.18.1 Installation

pip install flask-cors

1.18.2 Usage

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)  # Enable CORS for all routes

@app.route("/api/data")
def get_data():
    return {"message": "This is CORS enabled!"}

1.19 Testing

1.19.1 Using pytest

Installation:

pip install pytest pytest-flask

Test Example:

import pytest
from yourapp import app

@pytest.fixture
def client():
    app.config['TESTING'] = True
    with app.test_client() as client:
        yield client

def test_index_route(client):
    response = client.get('/')
    assert response.status_code == 200
    assert b'Hello, World!' in response.data

1.19.2 Using unittest

import unittest
from yourapp import app

class MyTestCase(unittest.TestCase):
    def setUp(self):
        app.config['TESTING'] = True
        self.app = app.test_client()

    def test_index_route(self):
        response = self.app.get('/')
        self.assertEqual(response.status_code, 200)
        self.assertIn(b'Hello, World!', response.data)

1.20 Deployment

1.20.1 Production Settings

  • Set FLASK_ENV=production to disable debug mode.
  • Use a production WSGI server (e.g., Gunicorn, uWSGI).
  • Configure your web server (e.g., Nginx, Apache) to proxy requests to the WSGI server.
  • Use a process manager (e.g., Supervisor, systemd) to manage the WSGI server.
  • Configure logging.
  • Use HTTPS.

1.20.2 WSGI Servers

Gunicorn:

pip install gunicorn
gunicorn yourapp:app --bind 0.0.0.0:8000

uWSGI:

pip install uwsgi
uwsgi --http 0.0.0.0:8000 --module yourapp

1.20.3 Environment Variables

Use environment variables for sensitive settings (e.g., SECRET_KEY, database credentials).

1.20.4 Example Deployment with Gunicorn and Nginx

  1. Install Gunicorn: pip install gunicorn
  2. Create a WSGI entry point: yourapp.py (already covered)
  3. Create a systemd service file: /etc/systemd/system/yourapp.service
[Unit]
Description=Gunicorn instance to serve yourapp
After=network.target

[Service]
User=youruser
Group=www-data
WorkingDirectory=/path/to/your/app
ExecStart=/path/to/your/venv/bin/gunicorn --workers 3 --max-requests 500 --bind unix:/run/yourapp.sock yourapp:app

[Install]
WantedBy=multi-user.target
  1. Create an Nginx configuration file: /etc/nginx/sites-available/yourapp
server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/yourapp.sock;
    }

    location /static {
        alias /path/to/your/app/static;
    }
}
  1. Create a symbolic link:
sudo ln -s /etc/nginx/sites-available/yourapp /etc/nginx/sites-enabled
  1. Restart Nginx:
sudo systemctl restart nginx

1.21 Security

  • Use a strong SECRET_KEY and keep it secret.
  • Use HTTPS.
  • Sanitize user input to prevent XSS attacks.
  • Use parameterized queries to prevent SQL injection.
  • Use a Content Security Policy (CSP) to prevent various attacks.
  • Protect against CSRF attacks using Flask-WTF.
  • Limit file upload sizes.
  • Validate file uploads.
  • Use a security linter (e.g., Bandit).

1.22 Logging

1.22.1 Configure Logging

import logging

logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# In your code:
logger.info('This is an info message')
logger.warning('This is a warning message')
logger.error('This is an error message')

1.22.2 Logging to a File

import logging
import logging.handlers

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# Create a file handler
log_handler = logging.handlers.RotatingFileHandler('yourapp.log', maxBytes=10240, backupCount=5)
log_handler.setLevel(logging.DEBUG)

# Create a logging format
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
log_handler.setFormatter(formatter)

# Add the handlers to the logger
logger.addHandler(log_handler)

1.23 Flask CLI

Flask provides a command-line interface for managing your application.

  • flask run: Runs the development server.
  • flask shell: Opens a Python shell with the Flask application context.
  • flask routes: Shows the registered routes.
  • flask db: Manages database migrations (requires Flask-Migrate).

To use the Flask CLI, you need to set the FLASK_APP environment variable:

export FLASK_APP=yourapp.py

Then, you can use the flask command:

flask run

1.24 Context Processors

Context processors inject variables automatically

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)  # Enable CORS for all routes

@app.route("/api/data")
def get_data():
    return {"message": "This is CORS enabled!"}

1.25 Testing

1.25.1 Using pytest

Installation:

pip install pytest pytest-flask

Test Example:

import pytest
from yourapp import app

@pytest.fixture
def client():
    app.config['TESTING'] = True
    with app.test_client() as client:
        yield client

def test_index_route(client):
    response = client.get('/')
    assert response.status_code == 200
    assert b'Hello, World!' in response.data

1.25.2 Using unittest

import unittest
from yourapp import app

class MyTestCase(unittest.TestCase):
    def setUp(self):
        app.config['TESTING'] = True
        self.app = app.test_client()

    def test_index_route(self):
        response = self.app.get('/')
        self.assertEqual(response.status_code, 200)
        self.assertIn(b'Hello, World!', response.data)

1.26 Deployment

1.26.1 Production Settings

  • Set FLASK_ENV=production to disable debug mode.
  • Use a production WSGI server (e.g., Gunicorn, uWSGI).
  • Configure your web server (e.g., Nginx, Apache) to proxy requests to the WSGI server.
  • Use a process manager (e.g., Supervisor, systemd) to manage the WSGI server.
  • Configure logging.
  • Use HTTPS.

1.26.2 WSGI Servers

Gunicorn:

pip install gunicorn
gunicorn yourapp:app --bind 0.0.0.0:8000

uWSGI:

pip install uwsgi
uwsgi --http 0.0.0.0:8000 --module yourapp

1.26.3 Environment Variables

Use environment variables for sensitive settings (e.g., SECRET_KEY, database credentials).

1.26.4 Example Deployment with Gunicorn and Nginx

  1. Install Gunicorn: pip install gunicorn
  2. Create a WSGI entry point: yourapp.py (already covered)
  3. Create a systemd service file: /etc/systemd/system/yourapp.service
[Unit]
Description=Gunicorn instance to serve yourapp
After=network.target

[Service]
User=youruser
Group=www-data
WorkingDirectory=/path/to/your/app
ExecStart=/path/to/your/venv/bin/gunicorn --workers 3 --max-requests 500 --bind unix:/run/yourapp.sock yourapp:app

[Install]
WantedBy=multi-user.target
  1. Create an Nginx configuration file: /etc/nginx/sites-available/yourapp
server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/yourapp.sock;
    }

    location /static {
        alias /path/to/your/app/static;
    }
}
  1. Create a symbolic link:
sudo ln -s /etc/nginx/sites-available/yourapp /etc/nginx/sites-enabled
  1. Restart Nginx:
sudo systemctl restart nginx

1.27 Security

  • Use a strong SECRET_KEY and keep it secret.
  • Use HTTPS.
  • Sanitize user input to prevent XSS attacks.
  • Use parameterized queries to prevent SQL injection.
  • Use a Content Security Policy (CSP) to prevent various attacks.
  • Protect against CSRF attacks using Flask-WTF.
  • Limit file upload sizes.
  • Validate file uploads.
  • Use a security linter (e.g., Bandit).

1.28 Logging

1.28.1 Configure Logging

import logging

logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# In your code:
logger.info('This is an info message')
logger.warning('This is a warning message')
logger.error('This is an error message')

1.28.2 Logging to a File

import logging
import logging.handlers

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# Create a file handler
log_handler = logging.handlers.RotatingFileHandler('yourapp.log', maxBytes=10240, backupCount=5)
log_handler.setLevel(logging.DEBUG)

# Create a logging format
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
log_handler.setFormatter(formatter)

# Add the handlers to the logger
logger.addHandler(log_handler)

1.29 Flask CLI

Flask provides a command-line interface for managing your application.

  • flask run: Runs the development server.
  • flask shell: Opens a Python shell with the Flask application context.
  • flask routes: Shows the registered routes.
  • flask db: Manages database migrations (requires Flask-Migrate).

To use the Flask CLI, you need to set the FLASK_APP environment variable:

export FLASK_APP=yourapp.py

Then, you can use the flask command:

flask run

1.30 Context Processors

Context processors inject variables automatically into all templates.

from flask import Flask, render_template

app = Flask(__name__)

@app.context_processor
def inject_variables():
    return dict(site_name="My Awesome Website")

@app.route('/')
def index():
    return render_template('index.html')

In templates/index.html:

<h1>Welcome to {{ site_name }}!</h1>

1.31 Error Handling

1.31.1 Custom Error Pages

from flask import Flask, render_template

app = Flask(__name__)

@app.errorhandler(404)
def page_not_found(e):
    return render_template('404.html'), 404

@app.errorhandler(500)
def internal_server_error(e):
    return render_template('500.html'), 500

1.31.2 Logging Exceptions

import logging
from flask import Flask, render_template

app = Flask(__name__)
logger = logging.getLogger(__name__)

@app.route('/')
def index():
    try:
        # Some code that might raise an exception
        raise ValueError("Something went wrong")
    except Exception as e:
        logger.exception("An error occurred")
        return render_template('error.html', error=str(e)), 500

1.32 Flask-RESTful

1.32.1 Installation

pip install flask-restful

1.32.2 Define Resources

from flask import Flask
from flask_restful import Api, Resource

app = Flask(__name__)
api = Api(app)

class HelloWorld(Resource):
    def get(self):
        return {'hello': 'world'}

api.add_resource(HelloWorld, '/')

1.32.3 Request Parsing

from flask_restful import reqparse

parser = reqparse.RequestParser()
parser.add_argument('name', required=True, help="Name is required")

class MyResource(Resource):
    def post(self):
        args = parser.parse_args()
        name = args['name']
        return {'message': f'Hello, {name}!'}

1.33 Session Management

from flask import Flask, session, redirect, url_for, escape, request

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'

@app.route('/')
def index():
    if 'username' in session:
        return f'Logged in as {escape(session["username"])}
Click here to <a href="{url_for("logout")}">logout</a>'
    return 'You are not logged in
Click here to <a href="{url_for("login")}">login</a>'

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        session['username'] = request.form['username']
        return redirect(url_for('index'))
    return '''
        <form method="post">
            <p><input type=text name=username>
            <p><input type=submit value=Login>
        </form>
    '''

@app.route('/logout')
def logout():
    session.pop('username', None)
    return redirect(url_for('index'))

1.34 Flask-CORS

1.34.1 Installation

pip install flask-cors

1.34.2 Usage

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)  # Enable CORS for all routes

@app.route("/api/data")
def get_data():
    return {"message": "This is CORS enabled!"}

1.35 Signals

Flask doesn't have built-in signals like Django, but you can use a third-party library like blinker to implement signals.

1.35.1 Installation

pip install blinker

1.35.2 Usage

from flask import Flask
from blinker import signal

app = Flask(__name__)

before_request = signal('before_request')

@app.before_request
def before_request_handler():
    before_request.send(app)

@before_request.connect
def my_listener(sender):
    print("Before request signal received")

@app.route('/')
def index():
    return "Hello, World!"

1.36 Flask-Limiter

1.36.1 Installation

pip install Flask-Limiter

1.36.2 Usage

from flask import Flask
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

app = Flask(__name__)
limiter = Limiter(
    app,
    key_func=get_remote_address,
    default_limits=["200 per day", "50 per hour"]
)

@app.route("/slow")
@limiter.limit("10 per minute")
def slow():
    return "Slow route"

@app.route("/fast")
def fast():
    return "Fast route"

1.37 Flask-APScheduler

1.37.1 Installation

pip install flask-apscheduler

1.37.2 Usage

from flask import Flask
from flask_apscheduler import APScheduler
import time

class Config(object):
    JOBS = [
        {
            'id': 'job1',
            'func': 'yourapp:job1',
            'trigger': 'interval',
            'seconds': 10
        }
    ]
    SCHEDULER_API_ENABLED = True

app = Flask(__name__)
app.config.from_object(Config())

scheduler = APScheduler()
# it is also possible to enable the API directly
# scheduler.api_enabled = True
scheduler.init_app(app)
scheduler.start()

def job1():
    print(time.strftime("%Y-%m-%d %H:%M:%S"))

if __name__ == '__main__':
    app.run(debug=True)

1.38 Flask-Sitemap

1.38.1 Installation

pip install Flask-Sitemap

1.38.2 Usage

from flask import Flask
from flask_sitemap import Sitemap

app = Flask(__name__)
ext = Sitemap(app=app)

@app.route("/sitemap.xml")
def sitemap():
    return ext.generate(base_url='http://example.com')

1.39 Flask-WTF CSRF Protection

1.39.1 Configuration

from flask import Flask
from flask_wtf.csrf import CSRFProtect

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'
csrf = CSRFProtect(app)

1.39.2 Usage in Templates

<form method="post">
    {{ form.csrf_token }}
    <!-- Your form fields -->
</form>

1.40 Flask-FlatPages

1.40.1 Installation

pip install Flask-FlatPages

1.40.2 Configuration

from flask import Flask
from flask_flatpages import FlatPages

app = Flask(__name__)
app.config['FLATPAGES_EXTENSION'] = '.md'
app.config['FLATPAGES_ROOT'] = 'pages'
pages = FlatPages(app)

1.40.3 Usage

Create a directory named pages in your project root. Add your flat pages as .md files.

from flask import Flask, render_template
from flask_flatpages import FlatPages, pygments_style_defs

app = Flask(__name__)
app.config['FLATPAGES_EXTENSION'] = '.md'
app.config['FLATPAGES_ROOT'] = 'pages'
app.config['FLATPAGES_MARKDOWN_EXTENSIONS'] = ['codehilite', 'fenced_code']
app.config['PYGMENTS_STYLE'] = 'default'
pages = FlatPages(app)

@app.route('/page/<path:path>')
def page(path):
    page = pages.get_or_404(path)
    return render_template('page.html', page=page, pygments_style=pygments_style_defs())

1.41 Flask-Assets

1.41.1 Installation

pip install Flask-Assets

1.41.2 Configuration

from flask import Flask
from flask_assets import Environment, Bundle

app = Flask(__name__)
assets = Environment(app)

js = Bundle('js/jquery.js', 'js/base.js', filters='jsmin', output='gen/packed.js')
css = Bundle('css/base.css', 'css/common.css', filters='cssmin', output='gen/all.css')

assets.register('all_js', js)
assets.register('all_css', css)

1.41.3 Usage in Templates

{% assets "all_js" %}
    <script type="text/javascript" src="{{ ASSET_URL }}"></script>
{% endassets %}

{% assets "all_css" %}
    <link rel="stylesheet" type="text/css" href="{{ ASSET_URL }}">
{% endassets %}

1.42 Flask-Babel

1.42.1 Installation

pip install Flask-Babel

1.42.2 Configuration

from flask import Flask
from flask_babel import Babel

app = Flask(__name__)
app.config['BABEL_DEFAULT_LOCALE'] = 'en'
babel = Babel(app)

1.42.3 Usage

from flask import Flask, render_template
from flask_babel import Babel, gettext

app = Flask(__name__)
app.config['BABEL_DEFAULT_LOCALE'] = 'en'
babel = Babel(app)

@app.route('/')
def index():
    title = gettext('Welcome')
    return render_template('index.html', title=title)

In templates/index.html:

<h1>{{ title }}</h1>

1.43 Flask-SocketIO

1.43.1 Installation

pip install flask-socketio

1.43.2 Usage

from flask import Flask, render_template
from flask_socketio import SocketIO, emit

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)

@app.route('/')
def index():
    return render_template('index.html')

@socketio.on('connect')
def test_connect():
    emit('my response', {'data': 'Connected!'})

@socketio.on('my event')
def handle_my_custom_event(json):
    print('received json: ' + str(json))
    socketio.emit('my response', json)

if __name__ == '__main__':
    socketio.run(app, debug=True)

In templates/index.html:

<!DOCTYPE html>
<html>
<head>
    <title>Flask-SocketIO Test</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js" integrity="sha512-q/dWj3kcmNeAqFvv3EY9JJ/KEvVcjtgJBmWsGGHa+YwdlOfjoOvozUvCpJlPzl5lwCDsLQIY9Mq1v8XtZiuCQ==" crossorigin="anonymous"></script>
    <script type="text/javascript" charset="utf-8">
        $(document).ready(function() {
            var socket = io();
            socket.on('connect', function() {
                socket.emit('my event', {data: 'I\'m connected!'});
            });
            socket.on('my response', function(msg) {
                $('#log').append('<p>Received: ' + msg.data + '</p>');
            });
            $('form#emit').submit(function(event) {
                socket.emit('my event', {data: $('#emit_data').val()});
                return false;
            });
        });
    </script>
</head>
<body>
    <h1>Flask-SocketIO Test</h1>
    <div id="log"></div>
    <form id="emit" method="POST" action="#">
        <input type="text" id="emit_data" name="emit_data" placeholder="Message">
        <input type="submit" value="Echo">
    </form>
</body>
</html>

1.44 Flask-Principal

1.44.1 Installation

pip install Flask-Principal

1.44.2 Usage

from flask import Flask, g
from flask_principal import Principal, Permission, RoleNeed, UserNeed, identity_loaded, UserContext, Identity, AnonymousIdentity

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'

principals = Principal(app)

# Define Needs
admin_permission = Permission(RoleNeed('admin'))
poster_permission = Permission(RoleNeed('poster'))

# Define Roles
admin_role = RoleNeed('admin')
poster_role = RoleNeed('poster')
user_need = UserNeed(1)

@identity_loaded.connect_via(app)
def on_identity_loaded(sender, identity):
    # Set the identity user object
    identity.user = get_user()

    # Add the UserNeed to the identity
    identity.provides.add(UserNeed(identity.user.id))

    # Assuming the user has a method that returns a list of roles
    for role in identity.user.roles:
        identity.provides.add(RoleNeed(role.name))

def get_user():
    # Replace with your user loading logic (e.g., from database)
    class User(object):
        def __init__(self, id, roles):
            self.id = id
            self.roles = roles

    class Role(object):
        def __init__(self, name):
            self.name = name

    admin_role = Role('admin')
    poster_role = Role('poster')

    user = User(1, [admin_role, poster_role])
    return user

@app.route('/')
def index():
    with UserContext(Identity(1)):
        if admin_permission.can():
            return "Admin access granted"
        elif poster_permission.can():
            return "Poster access granted"
        else:
            return "Access denied"

if __name__ == '__main__':
    app.run(debug=True)

1.45 Flask-JWT-Extended

1.45.1 Installation

pip install Flask-JWT-Extended

1.45.2 Usage

from flask import Flask
from flask_jwt_extended import JWTManager, create_access_token, jwt_required, get_jwt_identity

app = Flask(__name__)
app.config["JWT_SECRET_KEY"] = "super-secret"  # Change this!
jwt = JWTManager(app)

@app.route("/login", methods=["POST"])
def login():
    username = request.json.get("username", None)
    password = request.json.get("password", None)
    if username != "test" or password != "test":
        return jsonify({"msg": "Bad username or password"}), 401

    access_token = create_access_token(identity=username)
    return jsonify(access_token=access_token)

@app.route("/protected", methods=["GET"])
@jwt_required()
def protected():
    current_user = get_jwt_identity()
    return jsonify(logged_in_as=current_user), 200

1.46 Flask-Uploads

1.46.1 Installation

pip install Flask-Uploads

1.46.2 Usage

from flask import Flask, request, render_template
from flask_uploads import UploadSet, configure_uploads, IMAGES, patch_request_class

app = Flask(__name__)
app.config['UPLOADED_PHOTOS_DEST'] = 'uploads'
app.config['SECRET_KEY'] = 'super secret key'
photos = UploadSet('photos', IMAGES)

configure_uploads(app, photos)
patch_request_class(app)  # set maximum file size, default is 16MB

@app.route('/upload', methods=['GET', 'POST'])
def upload():
    if request.method == 'POST' and 'photo' in request.files:
        filename = photos.save(request.files['photo'])
        url = photos.url(filename)
        return render_template('upload.html', filename=filename, url=url)
    return render_template('upload.html')

In templates/upload.html:

<!doctype html>
<html>
<head>
    <title>Upload</title>
</head>
<body>
    {% if filename %}
        <img src="{{ url }}" alt="Uploaded Image">
    {% else %}
        <form method="post" enctype="multipart/form-data">
            <input type="file" name="photo">
            <button type="submit">Upload</button>
        </form>
    {% endif %}
</body>
</html>

1.47 Flask-Mail

1.47.1 Installation

pip install flask-mail

1.47.2 Configuration

from flask import Flask
from flask_mail import Mail, Message

app = Flask(__name__)
app.config['MAIL_SERVER'] = 'smtp.gmail.com'
app.config['MAIL_PORT'] = 587
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USE_SSL'] = False
app.config['MAIL_USERNAME'] = 'your_email@gmail.com'
app.config['MAIL_PASSWORD'] = 'your_password'
mail = Mail(app)

1.47.3 Sending Emails

from flask import Flask, render_template
from flask_mail import Mail, Message

app = Flask(__name__)
app.config['MAIL_SERVER'] = 'smtp.gmail.com'
app.config['MAIL_PORT'] = 587
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USE_SSL'] = False
app.config['MAIL_USERNAME'] = 'your_email@gmail.com'
app.config['MAIL_PASSWORD'] = 'your_password'
mail = Mail(app)

@app.route('/send')
def send_email():
    msg = Message("Hello",
                  sender="your_email@gmail.com",
                  recipients=["recipient@example.com"])
    msg.body = "Hello Flask message sent from Flask-Mail"
    mail.send(msg)
    return "Sent"

1.48 Flask-APScheduler

1.48.1 Installation

pip install flask-apscheduler

1.48.2 Usage

from flask import Flask
from flask_apscheduler import APScheduler
import time

class Config(object):
    JOBS = [
        {
            'id': 'job1',
            'func': 'yourapp:job1',
            'trigger': 'interval',
            'seconds': 10
        }
    ]
    SCHEDULER_API_ENABLED = True

app = Flask(__name__)
app.config.from_object(Config())

scheduler = APScheduler()
# it is also possible to enable the API directly
# scheduler.api_enabled = True
scheduler.init_app(app)
scheduler.start()

def job1():
    print(time.strftime("%Y-%m-%d %H:%M:%S"))

if __name__ == '__main__':
    app.run(debug=True)

1.49 Flask-Sitemap

1.49.1 Installation

pip install Flask-Sitemap

1.49.2 Usage

from flask import Flask
from flask_sitemap import Sitemap

app = Flask(__name__)
ext = Sitemap(app=app)

@app.route("/sitemap.xml")
def sitemap():
    return ext.generate(base_url='http://example.com')

1.50 Flask-WTF CSRF Protection

1.50.1 Configuration

from flask import Flask
from flask_wtf.csrf import CSRFProtect

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'
csrf = CSRFProtect(app)

1.50.2 Usage in Templates

<form method="post">
    {{ form.csrf_token }}
    <!-- Your form fields -->
</form>

1.51 Tips and Best Practices

  • Use virtual environments to isolate project dependencies.
  • Keep SECRET_KEY secure and out of your codebase. Use environment variables.
  • Use meaningful names for routes, variables, and functions.
  • Follow the DRY (Don't Repeat Yourself) principle.
  • Write unit tests to ensure code quality.
  • Use a production-ready web server (e.g., Gunicorn, uWSGI) and a process manager (e.g., Supervisor, systemd) for deployment.
  • Use a linter (like flake8) and formatter (like black) to ensure consistent code style.
  • Keep your code modular and reusable.
  • Document your code.
  • Use a version control system (e.g., Git).
  • Follow Flask's coding style guidelines.
  • Use Flask's built-in session management or a more robust solution like Flask-Session.
  • Monitor your application for errors and performance issues.
  • Use a CDN (Content Delivery Network) for static files.
  • Optimize database queries.
  • Use asynchronous tasks for long-running operations (e.g., sending emails) using Celery or similar.
  • Implement proper logging and error handling.
  • Regularly update Flask and its dependencies.
  • Use a security scanner to identify potential vulnerabilities.
  • Follow security best practices.
  • Use a reverse proxy like Nginx or Apache in front of your WSGI server.
  • Use a load balancer for high availability.
  • Automate deployments using tools like Fabric or Ansible.
  • Use a monitoring tool like Sentry or New Relic.
  • Implement health checks for your application.
  • Use a CDN for static assets.
  • Cache frequently accessed data.
  • Use a database connection pool.
  • Optimize your database queries.
  • Use a task queue for long-running tasks.
  • Use a background worker for asynchronous tasks.
  • Use a message queue for inter-process communication.
  • Use a service discovery tool for microservices.
  • Use a containerization tool like Docker.
  • Use an orchestration tool like Kubernetes.