Introduction to Flask Project Setup
Setting up a Flask application correctly is crucial for building maintainable and scalable web applications. In this lecture, we'll explore different project structures, configuration approaches, and environment setup techniques that help establish a solid foundation for Flask development.
"Good code organization is not just about aesthetics; it's about reducing cognitive load so you can focus on solving the problem at hand."
Why Project Structure Matters
A well-organized Flask project provides several benefits:
- Maintainability: Makes code easier to understand and modify
- Scalability: Allows the application to grow without becoming unwieldy
- Collaboration: Enables team members to work together efficiently
- Testability: Facilitates unit testing and integration testing
- Deployment: Simplifies the deployment process
Flask Project Structures
Flask gives you the freedom to structure your project how you want, but certain patterns have emerged as best practices depending on the size and complexity of your application.
Single Module Structure
For small applications or simple scripts, a single file might be sufficient:
# app.py
from flask import Flask, render_template
app = Flask(__name__)
# Configuration
app.config['SECRET_KEY'] = 'your-secret-key'
@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)
With a simple folder structure:
project/
├── app.py
├── static/
│ ├── css/
│ │ └── style.css
│ └── js/
│ └── script.js
├── templates/
│ ├── home.html
│ └── about.html
└── requirements.txt
Package Structure
As applications grow, organizing code into a package becomes more maintainable:
project/
├── app/
│ ├── __init__.py
│ ├── routes.py
│ ├── models.py
│ ├── forms.py
│ ├── static/
│ │ ├── css/
│ │ └── js/
│ └── templates/
│ ├── base.html
│ ├── home.html
│ └── about.html
├── config.py
├── run.py
└── requirements.txt
With this structure, the main application code is in app/__init__.py:
# app/__init__.py
from flask import Flask
app = Flask(__name__)
app.config.from_object('config.DevelopmentConfig')
from app import routes
Routes are defined in a separate module:
# app/routes.py
from flask import render_template
from app import app
@app.route('/')
def home():
return render_template('home.html')
@app.route('/about')
def about():
return render_template('about.html')
And the application is run from run.py:
# run.py
from app import app
if __name__ == '__main__':
app.run(debug=True)
Application Factory Pattern
For larger applications, using an application factory pattern provides more flexibility, especially for testing and managing multiple environments:
# app/__init__.py
from flask import Flask
def create_app(config_class=None):
app = Flask(__name__)
# Load default configuration
app.config.from_object('config.DevelopmentConfig')
# Override with provided configuration if available
if config_class:
app.config.from_object(config_class)
# Initialize extensions
# db.init_app(app)
# login_manager.init_app(app)
# Register blueprints
from app.main import bp as main_bp
app.register_blueprint(main_bp)
return app
With a corresponding project structure:
project/
├── app/
│ ├── __init__.py
│ ├── extensions.py
│ ├── models.py
│ ├── main/
│ │ ├── __init__.py
│ │ ├── routes.py
│ │ ├── forms.py
│ │ └── templates/
│ │ ├── main/
│ │ │ ├── home.html
│ │ │ └── about.html
│ ├── auth/
│ │ ├── __init__.py
│ │ ├── routes.py
│ │ ├── forms.py
│ │ └── templates/
│ │ ├── auth/
│ │ │ ├── login.html
│ │ │ └── register.html
│ ├── static/
│ │ ├── css/
│ │ └── js/
│ └── templates/
│ └── base.html
├── config.py
├── run.py
└── requirements.txt
Choosing the Right Structure
The best project structure depends on the size and complexity of your application:
- Single Module: Simple applications, scripts, or prototypes
- Package Structure: Small to medium-sized applications
- Application Factory + Blueprints: Larger applications, team projects, or applications requiring extensive testing
It's also common to start with a simpler structure and evolve it as your application grows.
Setting Up a Development Environment
Virtual Environments
Virtual environments are essential for Python development, allowing you to isolate project dependencies and avoid conflicts between different projects.
Using venv
# Create a virtual environment
python -m venv venv
# Activate the virtual environment (Windows)
venv\Scripts\activate
# Activate the virtual environment (macOS/Linux)
source venv/bin/activate
# Install Flask and other dependencies
pip install flask
# Deactivate when finished
deactivate
Using Pipenv
# Install Pipenv
pip install pipenv
# Initialize a project with Pipenv
pipenv install flask
# Activate the virtual environment
pipenv shell
# Add additional packages
pipenv install flask-sqlalchemy
# Exit the shell
exit
Using Poetry
# Install Poetry
pip install poetry
# Initialize a new project
poetry new flask-project
cd flask-project
# Add Flask as a dependency
poetry add flask
# Activate the virtual environment
poetry shell
# Exit the shell
exit
Managing Dependencies
Keeping track of your project's dependencies is crucial for reproducibility and deployment.
Using requirements.txt
# Generate requirements.txt
pip freeze > requirements.txt
# Install from requirements.txt
pip install -r requirements.txt
Using Pipenv's Pipfile
Pipenv automatically creates and updates Pipfile and Pipfile.lock files, which track dependencies and their exact versions.
Using Poetry's pyproject.toml
Poetry manages dependencies in the pyproject.toml file and creates a poetry.lock file for exact versions.
Real-World Example: Setting Up a Flask Project
Let's walk through a complete setup for a medium-sized Flask application using the package structure:
# 1. Create and activate a virtual environment
python -m venv venv
source venv/bin/activate # or venv\Scripts\activate on Windows
# 2. Install Flask and other dependencies
pip install flask flask-sqlalchemy flask-migrate flask-wtf python-dotenv
# 3. Create project structure
mkdir -p myapp/app/static/css myapp/app/static/js myapp/app/templates
# 4. Create basic files
cd myapp
touch app/__init__.py app/routes.py app/models.py config.py run.py .env .gitignore
# 5. Set up .gitignore
echo "venv/
__pycache__/
*.py[cod]
*$py.class
.env
.flaskenv
*.db
.DS_Store
.idea/
.vscode/" > .gitignore
# 6. Set up environment variables in .env
echo "FLASK_APP=run.py
FLASK_ENV=development
SECRET_KEY=your-secret-key-here
DATABASE_URL=sqlite:///app.db" > .env
# 7. Create a requirements.txt file
pip freeze > requirements.txt
Now let's implement the basic application files:
# config.py
import os
from dotenv import load_dotenv
basedir = os.path.abspath(os.path.dirname(__file__))
load_dotenv(os.path.join(basedir, '.env'))
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-key-for-development'
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'app.db')
SQLALCHEMY_TRACK_MODIFICATIONS = False
class DevelopmentConfig(Config):
DEBUG = True
class TestingConfig(Config):
TESTING = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
class ProductionConfig(Config):
DEBUG = False
config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig,
'default': DevelopmentConfig
}
# app/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from config import config
# Initialize extensions
db = SQLAlchemy()
migrate = Migrate()
def create_app(config_name='default'):
app = Flask(__name__)
app.config.from_object(config[config_name])
# Initialize extensions with app
db.init_app(app)
migrate.init_app(app, db)
# Register blueprints here (if any)
# Import and register routes
from app import routes
return app
# app/models.py
from datetime import datetime
from app import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, index=True)
email = db.Column(db.String(120), unique=True, index=True)
password_hash = db.Column(db.String(128))
created_at = db.Column(db.DateTime, default=datetime.utcnow)
def __repr__(self):
return f'<User {self.username}>'
# app/routes.py
from flask import render_template, current_app as app
from app.models import User
@app.route('/')
def home():
return render_template('home.html', title='Home')
@app.route('/about')
def about():
return render_template('about.html', title='About')
# run.py
import os
from app import create_app, db
app = create_app(os.getenv('FLASK_ENV', 'default'))
if __name__ == '__main__':
app.run()
Finally, let's create a basic template:
# app/templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }} - My Flask App</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
<nav>
<ul>
<li><a href="{{ url_for('home') }}">Home</a></li>
<li><a href="{{ url_for('about') }}">About</a></li>
</ul>
</nav>
<main>
{% block content %}{% endblock %}
</main>
<footer>
<p>© {{ year }} My Flask App</p>
</footer>
</body>
</html>
# app/templates/home.html
{% extends 'base.html' %}
{% block content %}
<h1>Welcome to My Flask App</h1>
<p>This is a sample Flask application.</p>
{% endblock %}
# app/templates/about.html
{% extends 'base.html' %}
{% block content %}
<h1>About</h1>
<p>This is a Flask application created for learning purposes.</p>
{% endblock %}
Configuration Management
Proper configuration management is essential for maintaining different environments (development, testing, production) and keeping sensitive information secure.
Configuration Approaches
Environment Variables
Using environment variables is a common approach for managing configuration:
# Loading environment variables
import os
from dotenv import load_dotenv
load_dotenv() # Load variables from .env file
# Access environment variables
SECRET_KEY = os.environ.get('SECRET_KEY', 'default-secret-key')
DEBUG = os.environ.get('FLASK_DEBUG', '0') == '1'
DATABASE_URL = os.environ.get('DATABASE_URL', 'sqlite:///app.db')
A typical .env file might look like:
# .env
FLASK_APP=run.py
FLASK_ENV=development
FLASK_DEBUG=1
SECRET_KEY=your-secret-key-here
DATABASE_URL=sqlite:///app.db
MAIL_SERVER=smtp.example.com
MAIL_PORT=587
MAIL_USERNAME=your-email@example.com
MAIL_PASSWORD=your-password
Security Note
Never commit .env files or any files containing sensitive information to version control. Always add them to your .gitignore file.
Configuration Classes
Using classes for configuration allows for inheritance and organization:
# config.py
import os
from dotenv import load_dotenv
basedir = os.path.abspath(os.path.dirname(__file__))
load_dotenv(os.path.join(basedir, '.env'))
class Config:
"""Base configuration."""
SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-key'
SQLALCHEMY_TRACK_MODIFICATIONS = False
MAIL_SERVER = os.environ.get('MAIL_SERVER', 'smtp.googlemail.com')
MAIL_PORT = int(os.environ.get('MAIL_PORT', 587))
MAIL_USE_TLS = os.environ.get('MAIL_USE_TLS', 'true').lower() in ['true', 'on', '1']
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
MAIL_DEFAULT_SENDER = os.environ.get('MAIL_DEFAULT_SENDER')
class DevelopmentConfig(Config):
"""Development configuration."""
DEBUG = True
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'dev.db')
class TestingConfig(Config):
"""Testing configuration."""
TESTING = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
WTF_CSRF_ENABLED = False
class ProductionConfig(Config):
"""Production configuration."""
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'prod.db')
@classmethod
def init_app(cls, app):
# Log to syslog
import logging
from logging.handlers import SysLogHandler
syslog_handler = SysLogHandler()
syslog_handler.setLevel(logging.WARNING)
app.logger.addHandler(syslog_handler)
config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig,
'default': DevelopmentConfig
}
Then in your application factory:
def create_app(config_name=None):
app = Flask(__name__)
# Determine configuration to use
if config_name is None:
config_name = os.environ.get('FLASK_ENV', 'default')
app.config.from_object(config[config_name])
config[config_name].init_app(app)
# ... rest of app initialization
Configuration From Files
For more complex configurations, you can use JSON or YAML files:
# Loading configuration from JSON
import json
with open('config.json') as f:
config = json.load(f)
app.config.update(config)
# Loading configuration from YAML
import yaml
with open('config.yaml') as f:
config = yaml.safe_load(f)
app.config.update(config)
Instance Folders
Flask supports an "instance folder" for configuration files that shouldn't be in version control:
app = Flask(__name__, instance_relative_config=True)
app.config.from_object('config.DevelopmentConfig')
app.config.from_pyfile('config.py', silent=True) # Load from instance/config.py
Development Tools and Workflows
Flask Development Server
Flask comes with a built-in development server that supports auto-reloading when code changes are detected:
# Running with the Flask command
export FLASK_APP=run.py
export FLASK_ENV=development
flask run
# Or via Python
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000)
Flask-CLI Extensions
The Flask command-line interface can be extended with custom commands:
# app/cli.py
import click
from flask.cli import with_appcontext
@click.command('init-db')
@with_appcontext
def init_db_command():
"""Initialize the database."""
from app import db
db.create_all()
click.echo('Initialized the database.')
Register the command in your application factory:
def create_app():
app = Flask(__name__)
# ...
from app.cli import init_db_command
app.cli.add_command(init_db_command)
return app
Debugging Tools
Flask Debug Toolbar
The Flask-DebugToolbar extension adds a debugging toolbar to your application:
pip install flask-debugtoolbar
# In your app
from flask_debugtoolbar import DebugToolbarExtension
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
app.config['DEBUG_TB_INTERCEPT_REDIRECTS'] = False
toolbar = DebugToolbarExtension(app)
Flask Shell
The Flask shell command provides an interactive Python shell with your application context already loaded:
export FLASK_APP=run.py
flask shell
You can customize the shell context:
@app.shell_context_processor
def make_shell_context():
return {'db': db, 'User': User, 'Post': Post}
Testing Setup
Flask makes it easy to set up testing with pytest:
# tests/conftest.py
import pytest
from app import create_app, db
@pytest.fixture
def app():
app = create_app('testing')
with app.app_context():
db.create_all()
yield app
db.drop_all()
@pytest.fixture
def client(app):
return app.test_client()
@pytest.fixture
def runner(app):
return app.test_cli_runner()
# tests/test_routes.py
def test_home_page(client):
response = client.get('/')
assert response.status_code == 200
assert b'Welcome' in response.data
Real-World Development Workflow
Here's a typical development workflow for a Flask project:
- Setup:
- Create and activate virtual environment
- Install dependencies
- Set up project structure
- Configure environment variables
- Development Cycle:
- Run the development server with
flask run - Make changes to code (Flask will auto-reload)
- Test changes in the browser
- Use Flask shell for debugging:
flask shell
- Run the development server with
- Database Changes:
- Update models
- Create migration:
flask db migrate -m "Description" - Apply migration:
flask db upgrade
- Testing:
- Write tests in
tests/directory - Run tests with pytest:
pytest - Generate coverage report:
pytest --cov=app
- Write tests in
- Deployment Preparation:
- Update dependencies:
pip freeze > requirements.txt - Set environment variables for production
- Run with a production WSGI server (Gunicorn, uWSGI)
- Update dependencies:
Practical Exercise
Setting Up a Flask Blog Application
Create a structured Flask application for a simple blog with the following components:
Requirements
- Use the package structure with application factory pattern
- Set up proper configuration for development and testing environments
- Include placeholders for database models (users and blog posts)
- Create basic templates with a layout template
- Implement routes for home page, about page, and blog listing
Steps
- Set up a virtual environment and install Flask
- Create the project structure:
blog/ ├── app/ │ ├── __init__.py │ ├── models.py │ ├── routes.py │ ├── static/ │ │ ├── css/ │ │ │ └── style.css │ │ └── js/ │ └── templates/ │ ├── base.html │ ├── index.html │ ├── about.html │ └── blog.html ├── config.py ├── run.py ├── .env └── requirements.txt - Implement the configuration class structure in
config.py - Create the application factory in
app/__init__.py - Define basic models in
app/models.py - Implement routes in
app/routes.py - Create templates with a common layout
- Set up the application entry point in
run.py
Bonus Challenges
- Add a custom CLI command to create a test blog post
- Implement a basic shell context for debugging
- Set up a simple test case for your routes
- Add Flask-DebugToolbar for development debugging
Summary
Key Takeaways
- Flask project structure should match the size and complexity of your application
- The application factory pattern provides flexibility for testing and multiple environments
- Configuration management is crucial for maintaining different environments
- Virtual environments keep dependencies isolated and managed
- Flask provides powerful development tools for debugging and testing
- A well-organized project structure facilitates maintainability and collaboration
Additional Resources
- Flask Application Factories
- Flask Configuration Handling
- Flask Command Line Interface
- Flask Testing
- Flask Tutorial Example
Next Lecture
In our next lecture, we'll explore routing and view functions in Flask, including URL patterns, route parameters, and response types.
Practice Activities
Basic Exercises
- Create a Flask application with the package structure
- Implement a configuration system with different environments
- Set up a virtual environment and requirements.txt file
- Create a basic template structure with inheritance
- Add a custom Flask CLI command
Advanced Project
Expand the blog application from the practical exercise:
- Reorganize it to use blueprints for different sections (main, blog, admin)
- Implement proper configuration with environment variables
- Add a shell context processor with models
- Set up basic tests for each blueprint
- Create a CLI command to initialize the database with sample data
- Add Flask-DebugToolbar for development