Skip to main content

Flask Project Structure

When you start developing web applications with Flask, one of the first challenges you'll face is deciding how to structure your project. A well-organized Flask project is easier to maintain, scale, and collaborate on. In this guide, we'll explore different approaches to structuring Flask applications, from simple single-file apps to more complex, modular structures.

Why Project Structure Matters

Having a good project structure provides several benefits:

  • Maintainability: Easier to read, understand, and update code
  • Scalability: Ability to grow your application without major refactoring
  • Collaboration: Allows teams to work on different parts of the application simultaneously
  • Testing: Makes it easier to write and run tests
  • Reusability: Encourages modular code that can be reused across projects

Simple Flask Application Structure

For beginners or very small applications, a single-file approach works fine:

python
# app.py
from flask import Flask, render_template

app = Flask(__name__)

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

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

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

With templates in a simple folder structure:

project/
├── app.py
├── static/
│ ├── css/
│ │ └── style.css
│ └── js/
│ └── script.js
└── templates/
├── index.html
└── about.html

This works for simple applications, but as your project grows, you'll need a more structured approach.

Basic Package Structure

As your application grows, you should move to a package structure. This separates concerns and makes your code more modular:

myapp/
├── __init__.py
├── routes.py
├── models.py
├── forms.py
├── static/
│ ├── css/
│ └── js/
└── templates/

Let's see how these files would work together:

python
# __init__.py
from flask import Flask

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

from myapp import routes
python
# routes.py
from flask import render_template
from myapp import app

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

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

To run this application, you'd create a run.py file in the parent directory:

python
# run.py
from myapp import app

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

Advanced Modular Structure

For larger applications, you'll want to use a modular structure with blueprints. Blueprints allow you to organize related functionality into separate components:

myapp/
├── __init__.py
├── config.py
├── extensions.py
├── models/
│ ├── __init__.py
│ ├── user.py
│ └── post.py
├── modules/
│ ├── __init__.py
│ ├── main/
│ │ ├── __init__.py
│ │ ├── routes.py
│ │ └── forms.py
│ ├── auth/
│ │ ├── __init__.py
│ │ ├── routes.py
│ │ └── forms.py
│ └── blog/
│ ├── __init__.py
│ ├── routes.py
│ └── forms.py
├── static/
├── templates/
│ ├── main/
│ ├── auth/
│ └── blog/
└── utils/
├── __init__.py
└── helpers.py

Here's how these files would work together:

python
# config.py
class Config:
SECRET_KEY = 'your-secret-key'
SQLALCHEMY_DATABASE_URI = 'sqlite:///site.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False

class DevelopmentConfig(Config):
DEBUG = True

class ProductionConfig(Config):
DEBUG = False

config = {
'development': DevelopmentConfig,
'production': ProductionConfig,
'default': DevelopmentConfig
}
python
# extensions.py
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager

db = SQLAlchemy()
login_manager = LoginManager()
python
# __init__.py
from flask import Flask
from myapp.config import config
from myapp.extensions import db, login_manager

def create_app(config_name='default'):
app = Flask(__name__)
app.config.from_object(config[config_name])

# Initialize extensions
db.init_app(app)
login_manager.init_app(app)

# Register blueprints
from myapp.modules.main import main_bp
from myapp.modules.auth import auth_bp
from myapp.modules.blog import blog_bp

app.register_blueprint(main_bp)
app.register_blueprint(auth_bp, url_prefix='/auth')
app.register_blueprint(blog_bp, url_prefix='/blog')

return app

Let's create a blueprint for the main module:

python
# modules/main/__init__.py
from flask import Blueprint

main_bp = Blueprint('main', __name__)

from . import routes
python
# modules/main/routes.py
from flask import render_template
from myapp.modules.main import main_bp

@main_bp.route('/')
def home():
return render_template('main/index.html')

@main_bp.route('/about')
def about():
return render_template('main/about.html')

To run this application, you'd create a run.py file:

python
# run.py
from myapp import create_app

app = create_app('development')

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

Project Structure for Production

For production applications, you might want to include additional files and folders:

myapp/
├── migrations/ # For database migrations using Flask-Migrate
├── tests/ # For unit tests
│ ├── __init__.py
│ ├── test_models.py
│ └── test_routes.py
├── .env # For environment variables (keep this out of version control!)
├── .gitignore
├── requirements.txt # For project dependencies
└── wsgi.py # Entry point for WSGI servers like Gunicorn

The wsgi.py file would be simple:

python
# wsgi.py
from myapp import create_app

app = create_app('production')

Real-World Example: Flask Blog Application

Let's see how we might structure a simple blog application:

flask_blog/
├── __init__.py
├── config.py
├── extensions.py
├── models/
│ ├── __init__.py
│ ├── user.py
│ └── post.py
├── blueprints/
│ ├── __init__.py
│ ├── main/
│ │ ├── __init__.py
│ │ └── routes.py
│ ├── auth/
│ │ ├── __init__.py
│ │ ├── routes.py
│ │ └── forms.py
│ └── blog/
│ ├── __init__.py
│ ├── routes.py
│ └── forms.py
├── static/
│ ├── css/
│ │ └── style.css
│ ├── js/
│ │ └── script.js
│ └── images/
├── templates/
│ ├── base.html
│ ├── main/
│ │ ├── index.html
│ │ └── about.html
│ ├── auth/
│ │ ├── login.html
│ │ └── register.html
│ └── blog/
│ ├── create_post.html
│ ├── edit_post.html
│ └── post_detail.html
├── requirements.txt
└── run.py

Here's part of our blog post model:

python
# models/post.py
from datetime import datetime
from flask_blog.extensions import db

class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
content = db.Column(db.Text, nullable=False)
date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)

def __repr__(self):
return f"Post('{self.title}', '{self.date_posted}')"

And part of our blog routes:

python
# blueprints/blog/routes.py
from flask import render_template, redirect, url_for, flash, request, abort
from flask_login import current_user, login_required
from flask_blog.models.post import Post
from flask_blog.extensions import db
from flask_blog.blueprints.blog import blog_bp
from flask_blog.blueprints.blog.forms import PostForm

@blog_bp.route('/post/new', methods=['GET', 'POST'])
@login_required
def new_post():
form = PostForm()
if form.validate_on_submit():
post = Post(title=form.title.data, content=form.content.data, author=current_user)
db.session.add(post)
db.session.commit()
flash('Your post has been created!', 'success')
return redirect(url_for('blog.post', post_id=post.id))
return render_template('blog/create_post.html', title='New Post', form=form)

Best Practices For Flask Project Structure

  1. Separate Configuration: Keep configuration separate from application code, and use different configurations for development, testing, and production.

  2. Use Application Factory Pattern: Create your Flask application with a function, allowing for easy testing and multiple instances.

  3. Organize by Feature: Group related functionality together (e.g., models, views, templates) rather than by type.

  4. Use Blueprints: Break your application into logical components with Flask blueprints.

  5. Keep Requirements Updated: Maintain a requirements.txt file and consider using pip-tools or poetry for dependency management.

  6. Environment Variables: Use .env files and python-dotenv to manage environment-specific variables.

  7. Include Tests: Always include tests in your project structure.

Common Pitfalls to Avoid

  • Circular Imports: Structure your application to avoid circular imports, which can cause difficult-to-debug errors.
  • Monolithic Files: Don't put too much code in a single file; break things down logically.
  • Hardcoded Configuration: Avoid hardcoding configuration values; use environment variables or configuration files.
  • Ignoring Tests: Don't leave testing as an afterthought; include it in your structure from the beginning.

Summary

A well-structured Flask application makes development easier, more maintainable, and more scalable. As your application grows, your structure should evolve from a simple single-file approach to a more modular structure using packages and blueprints.

Remember that the "best" structure depends on your specific project requirements. Flask's flexibility allows you to adapt your structure as needed, but having a solid foundation from the beginning will save you time and headaches later.

Additional Resources

Exercises

  1. Convert a simple single-file Flask application to a package structure.
  2. Create a Flask application with two blueprints: "main" and "admin".
  3. Implement a Flask project structure that includes models, forms, and templates for a simple to-do list application.
  4. Set up different configuration classes for development and production environments.
  5. Implement a Flask application factory pattern in your project.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)