Flask App Structure
When you first start building Flask applications, you might be tempted to put all your code in a single file. While this works for tiny applications, it quickly becomes unmanageable as your project grows. In this tutorial, we'll explore how to structure Flask applications for better maintainability, testability, and scalability.
Introduction
Flask is intentionally designed as a microframework, which means it doesn't enforce any particular project structure on developers. This flexibility is powerful, but it also means you need to make deliberate decisions about how to organize your code.
A well-structured Flask application makes it easier to:
- Add new features
- Fix bugs
- Collaborate with other developers
- Write tests
- Deploy your application
Let's explore different approaches to structuring Flask applications, starting from simple to more complex.
Single-File Application
For very small applications or prototypes, a single-file structure might be sufficient:
# app.py
from flask import Flask, render_template, request
app = Flask(__name__)
@app.route('/')
def home():
return render_template('home.html')
@app.route('/about')
def about():
return render_template('about.html')
if __name__ == '__main__':
app.run(debug=True)
This simple structure works well for:
- Learning Flask basics
- Building quick prototypes
- Simple applications with just a few routes
However, as your application grows, this approach becomes problematic. Let's look at better structures for larger applications.
Basic Package Structure
For slightly larger applications, organizing code into a package structure provides better separation of concerns:
my_flask_app/
├── app/
│ ├── __init__.py
│ ├── routes.py
│ ├── models.py
│ ├── forms.py
│ ├── static/
│ └── templates/
├── config.py
├── requirements.txt
└── run.py
Let's see how this works:
# app/__init__.py
from flask import Flask
app = Flask(__name__)
app.config.from_object('config')
from app import routes
# app/routes.py
from app import app
from flask import render_template
@app.route('/')
def home():
return render_template('home.html')
@app.route('/about')
def about():
return render_template('about.html')
# config.py
DEBUG = True
SECRET_KEY = 'your-secret-key'
# run.py
from app import app
if __name__ == '__main__':
app.run()
This structure separates your application code from the entry point (run.py
). The app
package contains all your application code, and config.py
contains configuration variables.
Blueprint-Based Structure
As applications grow even larger, using Flask blueprints helps organize code by feature or functionality:
my_flask_app/
├── app/
│ ├── __init__.py
│ ├── models/
│ │ ├── __init__.py
│ │ └── user.py
│ ├── static/
│ ├── templates/
│ └── views/
│ ├── __init__.py
│ ├── auth.py
│ └── main.py
├── config.py
├── requirements.txt
└── run.py
Here's how blueprints work:
# app/views/main.py
from flask import Blueprint, render_template
main = Blueprint('main', __name__)
@main.route('/')
def home():
return render_template('main/home.html')
@main.route('/about')
def about():
return render_template('main/about.html')
# app/views/auth.py
from flask import Blueprint, render_template, redirect, url_for
auth = Blueprint('auth', __name__, url_prefix='/auth')
@auth.route('/login')
def login():
return render_template('auth/login.html')
@auth.route('/register')
def register():
return render_template('auth/register.html')
# app/__init__.py
from flask import Flask
def create_app(config_name='default'):
app = Flask(__name__)
# Load configuration
from config import config
app.config.from_object(config[config_name])
# Register blueprints
from app.views.main import main
from app.views.auth import auth
app.register_blueprint(main)
app.register_blueprint(auth)
return app
# run.py
from app import create_app
app = create_app()
if __name__ == '__main__':
app.run()
Blueprints allow you to:
- Group related functionality (like authentication, API endpoints, etc.)
- Reuse code across different applications
- Keep your codebase organized as it grows
Application Factory Pattern
For even more flexibility, especially when writing tests or running multiple instances of the same application, the Application Factory pattern is recommended:
# app/__init__.py
from flask import Flask
def create_app(config_name='default'):
app = Flask(__name__)
# Load configuration
from config import config
app.config.from_object(config[config_name])
# Initialize extensions
from app.extensions import db, migrate
db.init_app(app)
migrate.init_app(app, db)
# Register blueprints
from app.views.main import main_blueprint
from app.views.auth import auth_blueprint
app.register_blueprint(main_blueprint)
app.register_blueprint(auth_blueprint)
return app
This pattern allows you to create multiple instances of your application with different configurations, which is particularly useful for testing.
Real-World Example: Todo Application
Let's implement a simple Todo application using the blueprint structure:
todo_app/
├── app/
│ ├── __init__.py
│ ├── extensions.py
│ ├── models/
│ │ ├── __init__.py
│ │ └── todo.py
│ ├── static/
│ │ └── css/
│ │ └── style.css
│ ├── templates/
│ │ ├── base.html
│ │ └── todos/
│ │ ├── index.html
│ │ └── create.html
│ └── views/
│ ├── __init__.py
│ └── todos.py
├── config.py
├── requirements.txt
└── run.py
Here's how the implementation might look:
# app/extensions.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
# app/models/todo.py
from app.extensions import db
from datetime import datetime
class Todo(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
description = db.Column(db.Text, nullable=True)
completed = db.Column(db.Boolean, default=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
def __repr__(self):
return f'<Todo {self.id}: {self.title}>'
# app/views/todos.py
from flask import Blueprint, render_template, request, redirect, url_for
from app.extensions import db
from app.models.todo import Todo
todos_bp = Blueprint('todos', __name__, url_prefix='/todos')
@todos_bp.route('/')
def index():
todos = Todo.query.all()
return render_template('todos/index.html', todos=todos)
@todos_bp.route('/create', methods=['GET', 'POST'])
def create():
if request.method == 'POST':
title = request.form.get('title')
description = request.form.get('description')
if title:
todo = Todo(title=title, description=description)
db.session.add(todo)
db.session.commit()
return redirect(url_for('todos.index'))
return render_template('todos/create.html')
@todos_bp.route('/<int:id>/toggle', methods=['POST'])
def toggle(id):
todo = Todo.query.get_or_404(id)
todo.completed = not todo.completed
db.session.commit()
return redirect(url_for('todos.index'))
# app/__init__.py
from flask import Flask
def create_app(config_name='default'):
app = Flask(__name__)
# Load configuration
from config import config
app.config.from_object(config[config_name])
# Initialize extensions
from app.extensions import db
db.init_app(app)
# Register blueprints
from app.views.todos import todos_bp
app.register_blueprint(todos_bp)
# Create database tables
with app.app_context():
db.create_all()
return app
# config.py
import os
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY', 'dev-key')
SQLALCHEMY_TRACK_MODIFICATIONS = False
class DevelopmentConfig(Config):
DEBUG = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///dev.db'
class TestingConfig(Config):
TESTING = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///test.db'
class ProductionConfig(Config):
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL')
config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig,
'default': DevelopmentConfig
}
# run.py
from app import create_app
app = create_app()
if __name__ == '__main__':
app.run()
This Todo application demonstrates:
- Blueprint organization
- Application factory pattern
- Database models with SQLAlchemy
- Configuration management
- Separation of concerns
Best Practices for Flask Application Structure
Here are some best practices to follow when structuring your Flask applications:
-
Separation of Concerns: Separate your code into logical components like models, views, forms, and utilities.
-
Blueprint Organization: Group related functionality into blueprints, either by feature (auth, admin, api) or by resource (users, posts, comments).
-
Configuration Management: Use different configurations for development, testing, and production environments.
-
Application Factory Pattern: Create your Flask application using a factory function to enable easier testing and multiple instances.
-
Extensions Initialization: Initialize Flask extensions outside of your application factory, then initialize them with your app inside the factory.
-
Keep Templates and Static Files Organized: Organize templates and static files by blueprint or feature.
-
Tests Directory: Keep tests separate from your application code, typically in a
tests/
directory at the project root.
Summary
A well-structured Flask application makes development and maintenance much easier as your project grows. While Flask gives you the freedom to structure your application however you want, following established patterns like blueprints and the application factory can save you time and headaches in the long run.
As your Flask applications become more complex, consider these different structural approaches:
- Single-file for tiny applications and prototypes
- Basic package structure for small applications
- Blueprint-based organization for medium-sized applications
- Application factory pattern for large applications and when writing tests
Remember, the best structure depends on your specific needs, and it's okay to evolve your structure as your application grows.
Additional Resources
- Official Flask Documentation on Application Structure
- Miguel Grinberg's Flask Mega-Tutorial
- Explore Flask - Project Structure
Exercises
- Convert a single-file Flask application into a package structure with separate files for routes, models, and configuration.
- Create a Flask application with two blueprints: one for user authentication and another for a blog feature.
- Refactor an existing Flask application to use the application factory pattern.
- Create a simple Flask application with proper structure that includes:
- User authentication
- Database models
- CRUD operations
- Blueprint organization
By mastering Flask's application structure, you'll be well on your way to building maintainable, scalable web applications with Flask!
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)