Flask Blueprints
Table of Contents
Introduction to Blueprints
Blueprints are one of Flask's most powerful features. They allow you to organize a Flask application into modular and reusable components, each implementing a distinct set of functionalities.
Official Definition
According to Flask's official documentation, a Blueprint is:
"""
An object that works similarly to a Flask application,
but it's not actually an application.
It's rather a 'blueprint' describing how to construct or extend an application.
"""
Why Use Blueprints
Advantage | Description |
---|---|
Modularity | Divide your application into independent logical modules for better organization and maintenance. |
Reusability | Create components that can be reused across different applications or projects. |
Namespaces | Avoid URL name conflicts and group related routes under a common prefix. |
Separation of Concerns | Isolate business logic by functional domain (authentication, API, admin pages, etc.). |
Scalability | Facilitate progressive growth of your application by adding distinct blueprints. |
Code Clarity | Improve readability and maintainability by organizing code logically. |
When to Use Blueprints?
Basic Setup
Here's how to configure Blueprints in a Flask application:
1. Create a Blueprint
Let's start by creating a simple blueprint to handle authentication routes:
# app/routes/auth.py
from flask import Blueprint, render_template, redirect, url_for, flash, request
from flask_login import login_user, logout_user, login_required, current_user
# Create a blueprint with a name and template prefix
auth_bp = Blueprint(
'auth', # Blueprint name
__name__, # Where the blueprint is defined
template_folder='templates/auth', # Templates folder (optional)
static_folder='static/auth', # Static files folder (optional)
url_prefix='/auth' # URL prefix for all blueprint routes
)
# Add routes to the blueprint
@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('main.index'))
if request.method == 'POST':
# Authentication logic
# ...
flash('Login successful!', 'success')
return redirect(url_for('main.index'))
return render_template('auth/login.html')
@auth_bp.route('/logout')
@login_required
def logout():
logout_user()
flash('Logout successful!', 'success')
return redirect(url_for('auth.login'))
@auth_bp.route('/register', methods=['GET', 'POST'])
def register():
# Registration logic
# ...
return render_template('auth/register.html')
2. Register the Blueprint with the Application
Once the blueprint is created, you need to register it with your Flask application:
# app/__init__.py
from flask import Flask
def create_app():
app = Flask(__name__)
app.config.from_object('config.Config')
# Initialize extensions
# ...
# Register blueprints
from app.routes.auth import auth_bp
from app.routes.main import main_bp
app.register_blueprint(auth_bp)
app.register_blueprint(main_bp)
return app
Important Parameters When Creating a Blueprint
- name - A unique identifier for the blueprint
- import_name - Usually __name__, used to resolve relative paths
- static_folder - Folder for blueprint-specific static files
- template_folder - Folder for blueprint-specific templates
- url_prefix - Added to the beginning of all blueprint URLs
- subdomain - Limits the blueprint to a specific subdomain
- url_defaults - A dictionary of default values for URL parameters
URL Prefixes and Names
URL prefixes and blueprint names play an important role in organizing routes and generating URLs.
URL Prefixes
URL prefixes allow you to group all routes of a blueprint under a common path:
# Creating a blueprint with a URL prefix
api_bp = Blueprint('api', __name__, url_prefix='/api/v1')
# All routes will be prefixed with /api/v1
@api_bp.route('/users') # Accessible via /api/v1/users
def get_users():
pass
@api_bp.route('/posts') # Accessible via /api/v1/posts
def get_posts():
pass
Namespaces and url_for()
Blueprints create namespaces for route names. To generate URLs to routes defined in a blueprint, use the blueprint_name.view_function_name
syntax:
from flask import url_for
# URL to a route in the auth blueprint
login_url = url_for('auth.login')
# URL to a route in the main blueprint
home_url = url_for('main.index')
# URL with parameters
profile_url = url_for('user.profile', username='john')
Blueprint Resources
Blueprints can have their own resources, such as static files and templates, independently from the main application.
Static Files in Blueprints
Each blueprint can have its own static files folder:
# File structure
blueprint_folder/
├── static/
│ ├── css/
│ │ └── style.css
│ ├── js/
│ │ └── script.js
│ └── img/
│ └── logo.png
└── __init__.py
# Creating the blueprint with a static folder
from flask import Blueprint
admin_bp = Blueprint('admin', __name__,
static_folder='static',
static_url_path='/admin-static') # Optional URL
# Usage in a template
Blueprint-Specific Templates
Blueprints can also have their own templates:
# File structure
blueprint_folder/
├── templates/
│ ├── admin/ # Optional subfolder to avoid name conflicts
│ │ ├── dashboard.html
│ │ └── users.html
└── __init__.py
# Creating the blueprint with a templates folder
from flask import Blueprint, render_template
admin_bp = Blueprint('admin', __name__,
template_folder='templates')
@admin_bp.route('/dashboard')
def dashboard():
# Rendering a template from the blueprint folder
return render_template('admin/dashboard.html')
# Note that Flask looks first in the application's templates folder
# before looking in the blueprint's templates folder
Hooks and Middleware
Blueprints have their own hooks, similar to those of the Flask application, but with a scope limited to the blueprint.
# app/blueprints/auth/__init__.py
from flask import Blueprint, g, session, redirect, url_for, request
auth_bp = Blueprint('auth', __name__, url_prefix='/auth')
# Executed before each request to the blueprint
@auth_bp.before_request
def check_authentication():
"""Check if the user is authenticated for routes in this blueprint"""
if not session.get('user_id') and request.endpoint != 'auth.login':
return redirect(url_for('auth.login'))
# Executed after each request to the blueprint, even in case of error
@auth_bp.after_request
def add_header(response):
"""Add additional headers to responses from this blueprint"""
response.headers['X-Auth-Version'] = '1.0'
return response
# Executed if an unhandled exception is raised in the blueprint
@auth_bp.errorhandler(404)
def page_not_found(e):
"""Custom 404 error handler for this blueprint"""
return render_template('auth/404.html'), 404
# Modifies the template context for templates rendered by this blueprint
@auth_bp.context_processor
def inject_user():
"""Inject the current_user variable into all templates of this blueprint"""
if session.get('user_id'):
user = User.query.get(session['user_id'])
return {'current_user': user}
return {'current_user': None}
Design Patterns
Blueprints allow you to implement several advanced design patterns in your Flask applications.
Functional Model
Organization by business function:
app/
├── blueprints/
│ ├── auth/ # Authentication and user management
│ ├── blog/ # Blog features
│ ├── shop/ # Store features
│ └── admin/ # Administration
Request Type Model
Separation of API and web interfaces:
app/
├── blueprints/
│ ├── api/ # API endpoints for mobile/JS clients
│ └── web/ # Traditional web pages with templates
Testing Blueprints
Blueprints also facilitate unit and integration testing, as they allow you to test each module in isolation.
import pytest
from app import create_app
from app.blueprints.auth import auth_bp
@pytest.fixture
def client():
"""Test client for the auth blueprint only"""
app = create_app()
# Register only the blueprint we want to test
app.register_blueprint(auth_bp)
with app.test_client() as client:
yield client
def test_login_page(client):
"""Test that the login page displays correctly"""
response = client.get('/auth/login')
assert response.status_code == 200
assert b'Login' in response.data
def test_register_page(client):
"""Test that the registration page displays correctly"""
response = client.get('/auth/register')
assert response.status_code == 200
assert b'Register' in response.data
Testing Advantage
One of the major advantages of blueprints for testing is the ability to test each component in isolation, without having to load the entire application. This speeds up tests and makes them more accurate.