- Introduction to Flask
- Setting Up Your Development Environment
- Basic Flask Concepts
- Routing and Views
- Templates and Jinja2
- Working with Forms
- Database Integration
- User Authentication
- RESTful API Development
- Flask Extensions
- Testing Flask Applications
- Deployment
- Best Practices
- Advanced Topics
Flask is a lightweight WSGI web application framework written in Python. It's designed to be simple and easy to use, while also being powerful enough to build complex web applications. Created by Armin Ronacher, Flask is often referred to as a "micro" framework because it doesn't require particular tools or libraries, giving developers the flexibility to choose their preferred tools and libraries.
- Lightweight and modular design
- Built-in development server and debugger
- RESTful request dispatching
- Jinja2 templating engine
- Secure cookies support
- Unicode-based
- Extensive documentation
- Large community and ecosystem
- Python 3.7 or higher
- pip (Python package installer)
- Virtual environment tool (venv or virtualenv)
- Create a new project directory
mkdir myflaskapp
cd myflaskapp
- Create and activate a virtual environment
# On Windows
python -m venv venv
venv\Scripts\activate
# On macOS/Linux
python3 -m venv venv
source venv/bin/activate
- Install Flask
pip install flask
- Create requirements.txt
pip freeze > requirements.txt
- Basic Project Structure
myflaskapp/
├── venv/
├── static/
│ ├── css/
│ ├── js/
│ └── images/
├── templates/
├── instance/
├── app/
│ ├── __init__.py
│ ├── routes.py
│ ├── models.py
│ └── forms.py
├── config.py
├── requirements.txt
└── run.py
- Create app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == '__main__':
app.run(debug=True)
- Run the application
python app.py
Flask uses contexts to make certain variables globally accessible to a thread without interfering with other threads. There are two types of contexts:
- Application Context
current_app
: The active application instanceg
: Object for storing temporary data during request
- Request Context
request
: Contains HTTP request datasession
: User session dictionary
@app.route('/')
def index():
return 'Index Page'
@app.route('/hello')
def hello():
return 'Hello, World'
@app.route('/user/<username>')
def show_user_profile(username):
return f'User {username}'
@app.route('/post/<int:post_id>')
def show_post(post_id):
return f'Post {post_id}'
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
return do_login()
else:
return show_login_form()
from flask import render_template
@app.route('/hello/<name>')
def hello(name):
return render_template('hello.html', name=name)
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<h1>Hello, {{ name }}!</h1>
</body>
</html>
base.html
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
child.html
{% extends "base.html" %}
{% block title %}Page Title{% endblock %}
{% block content %}
<h1>This is the content</h1>
{% endblock %}
pip install flask-wtf
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Email
class LoginForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()])
submit = SubmitField('Log In')
@app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
# Process the form data
return redirect(url_for('index'))
return render_template('login.html', form=form)
pip install flask-sqlalchemy
from flask_sqlalchemy import SQLAlchemy
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
db = SQLAlchemy(app)
class User(db.Model):
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)
posts = db.relationship('Post', backref='author', lazy=True)
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)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
# Create
new_user = User(username='john', email='[email protected]')
db.session.add(new_user)
db.session.commit()
# Read
user = User.query.filter_by(username='john').first()
# Update
user.email = '[email protected]'
db.session.commit()
# Delete
db.session.delete(user)
db.session.commit()
pip install flask-login
from flask_login import LoginManager, UserMixin
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'
class User(UserMixin, db.Model):
# User model implementation
pass
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
from flask_login import login_user, logout_user, login_required
@app.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('index'))
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user and user.check_password(form.password.data):
login_user(user, remember=form.remember_me.data)
return redirect(url_for('index'))
return render_template('login.html', form=form)
@app.route('/logout')
@login_required
def logout():
logout_user()
return redirect(url_for('index'))
from flask import jsonify, request
@app.route('/api/users', methods=['GET'])
def get_users():
users = User.query.all()
return jsonify([{
'id': user.id,
'username': user.username,
'email': user.email
} for user in users])
@app.route('/api/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
user = User.query.get_or_404(user_id)
return jsonify({
'id': user.id,
'username': user.username,
'email': user.email
})
@app.errorhandler(404)
def not_found_error(error):
return jsonify({'error': 'Not found'}), 404
@app.errorhandler(500)
def internal_error(error):
db.session.rollback()
return jsonify({'error': 'Internal server error'}), 500
- Flask-SQLAlchemy: Database ORM
- Flask-Login: User session management
- Flask-WTF: Form handling and validation
- Flask-Mail: Email support
- Flask-Migrate: Database migrations
- Flask-RESTful: REST API building
- Flask-Admin: Admin interface
- Flask-Security: Security features
pip install flask-sqlalchemy flask-login flask-wtf flask-mail flask-migrate flask-restful flask-admin flask-security
import unittest
from app import app, db
class TestConfig:
TESTING = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
class UserModelCase(unittest.TestCase):
def setUp(self):
app.config.from_object(TestConfig)
self.app = app.test_client()
db.create_all()
def tearDown(self):
db.session.remove()
db.drop_all()
def test_password_hashing(self):
u = User(username='test')
u.set_password('test')
self.assertFalse(u.check_password('wrong'))
self.assertTrue(u.check_password('test'))
pip install gunicorn
from app import app
if __name__ == "__main__":
app.run()
gunicorn -w 4 wsgi:app
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
- Use blueprints for large applications
- Implement proper error handling
- Use configuration files
- Follow PEP 8 style guide
- Implement logging
- Use environment variables
- Write tests
- Use HTTPS
- Implement CSRF protection
- Sanitize user input
- Use secure password hashing
- Implement rate limiting
- Keep dependencies updated
from flask import Flask
from asgiref.wsgi import WsgiToAsgi
app = Flask(__name__)
asgi_app = WsgiToAsgi(app)
@app.route('/')
async def index():
result = await async_operation()
return result
from flask_socketio import SocketIO, emit
socketio = SocketIO(app)
@socketio.on('connect')
def handle_connect():
emit('response', {'data': 'Connected'})
@socketio.on('message')
def handle_message(message):
emit('response', {'data': f'Received: {message}'})
from flask_caching import Cache
cache = Cache(app, config={'CACHE_TYPE': 'simple'})
@app.route('/expensive-operation')
@cache.cached(timeout=300)
def expensive_operation():
# Perform expensive operation
return result
- Learn Python basics
- Understand web development concepts
- Complete Flask's official tutorial
- Build simple CRUD applications
- Learn about templates and forms
- Database integration with SQLAlchemy
- User authentication and authorization
- RESTful API development
- Flask extensions
- Testing Flask applications
- Application architecture and design patterns
- Performance optimization
- Deployment strategies
- Security best practices
- Asynchronous Flask
- WebSocket implementation
- Microservices architecture
- Official Flask Documentation
- Flask Mega-Tutorial by Miguel Grinberg
- Flask Web Development by Miguel Grinberg
- Flask community on Discord and Stack Overflow
- GitHub repositories with Flask examples
Flask's simplicity and flexibility make it an excellent choice for both beginners and experienced developers. As you progress in your Flask journey, remember to:
- Start with the basics and gradually move to advanced topics
- Practice by building real-world applications
- Participate in the Flask community
- Keep security in mind
- Stay updated with Flask developments
- Document your code
- Write tests for your applications
This guide provides a solid foundation for Flask development, but remember that practical experience is crucial for mastering the framework. Happy coding!
A full-featured blog with user authentication, post creation, comments, and admin panel.
blog/
├── app/
│ ├── __init__.py
│ ├── auth/
│ │ ├── __init__.py
│ │ ├── forms.py
│ │ └── routes.py
│ ├── main/
│ │ ├── __init__.py
│ │ ├── forms.py
│ │ └── routes.py
│ ├── models.py
│ ├── templates/
│ │ ├── base.html
│ │ ├── auth/
│ │ └── main/
│ └── static/
├── config.py
├── requirements.txt
└── run.py
- Database Models (models.py)
from datetime import datetime
from app import db, login_manager
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password_hash = db.Column(db.String(128))
posts = db.relationship('Post', backref='author', lazy='dynamic')
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)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(140), nullable=False)
content = db.Column(db.Text, nullable=False)
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
comments = db.relationship('Comment', backref='post', lazy='dynamic')
class Comment(db.Model):
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.Text, nullable=False)
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
- Authentication Routes (auth/routes.py)
from flask import Blueprint, render_template, redirect, url_for, flash
from flask_login import login_user, logout_user, login_required
from app.auth.forms import LoginForm, RegistrationForm
from app.models import User
from app import db
auth = Blueprint('auth', __name__)
@auth.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
user = User(username=form.username.data, email=form.email.data)
user.set_password(form.password.data)
db.session.add(user)
db.session.commit()
flash('Registration successful!')
return redirect(url_for('auth.login'))
return render_template('auth/register.html', form=form)
@auth.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user and user.check_password(form.password.data):
login_user(user, remember=form.remember_me.data)
return redirect(url_for('main.index'))
flash('Invalid email or password')
return render_template('auth/login.html', form=form)
- Blog Routes (main/routes.py)
from flask import Blueprint, render_template, redirect, url_for, flash, request
from flask_login import current_user, login_required
from app.main.forms import PostForm, CommentForm
from app.models import Post, Comment
from app import db
main = Blueprint('main', __name__)
@main.route('/')
def index():
page = request.args.get('page', 1, type=int)
posts = Post.query.order_by(Post.timestamp.desc()).paginate(
page=page, per_page=10)
return render_template('main/index.html', posts=posts)
@main.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!')
return redirect(url_for('main.index'))
return render_template('main/create_post.html', form=form)
@main.route('/post/<int:post_id>')
def post(post_id):
post = Post.query.get_or_404(post_id)
comments = post.comments.order_by(Comment.timestamp.desc())
form = CommentForm()
return render_template('main/post.html', post=post,
comments=comments, form=form)
- Templates (templates/main/index.html)
{% extends "base.html" %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-8">
{% for post in posts.items %}
<article class="post">
<h2><a href="{{ url_for('main.post', post_id=post.id) }}">
{{ post.title }}
</a></h2>
<p class="post-meta">
Posted by {{ post.author.username }} on
{{ post.timestamp.strftime('%Y-%m-%d') }}
</p>
<div class="post-content">
{{ post.content[:200] }}...
</div>
</article>
{% endfor %}
<!-- Pagination -->
<nav aria-label="Page navigation">
<ul class="pagination">
{% if posts.has_prev %}
<li class="page-item">
<a class="page-link"
href="{{ url_for('main.index', page=posts.prev_num) }}">
Previous
</a>
</li>
{% endif %}
{% if posts.has_next %}
<li class="page-item">
<a class="page-link"
href="{{ url_for('main.index', page=posts.next_num) }}">
Next
</a>
</li>
{% endif %}
</ul>
</nav>
</div>
</div>
</div>
{% endblock %}
A RESTful API service for managing a product inventory system.
api/
├── app/
│ ├── __init__.py
│ ├── models.py
│ ├── routes/
│ │ ├── __init__.py
│ │ ├── auth.py
│ │ └── products.py
│ ├── schemas.py
│ └── utils.py
├── config.py
├── requirements.txt
└── run.py
- Models (models.py)
from app import db
from datetime import datetime
class Product(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
description = db.Column(db.Text)
price = db.Column(db.Float, nullable=False)
stock = db.Column(db.Integer, default=0)
category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
created_at = db.Column(db.DateTime, default=datetime.utcnow)
updated_at = db.Column(db.DateTime, default=datetime.utcnow,
onupdate=datetime.utcnow)
class Category(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
products = db.relationship('Product', backref='category', lazy=True)
- Schemas (schemas.py)
from marshmallow import Schema, fields
class CategorySchema(Schema):
id = fields.Int(dump_only=True)
name = fields.Str(required=True)
class ProductSchema(Schema):
id = fields.Int(dump_only=True)
name = fields.Str(required=True)
description = fields.Str()
price = fields.Float(required=True)
stock = fields.Int(required=True)
category_id = fields.Int(required=True)
created_at = fields.DateTime(dump_only=True)
updated_at = fields.DateTime(dump_only=True)
- Routes (routes/products.py)
from flask import Blueprint, jsonify, request
from app.models import Product, Category
from app.schemas import ProductSchema, CategorySchema
from app import db
api = Blueprint('api', __name__)
product_schema = ProductSchema()
products_schema = ProductSchema(many=True)
@api.route('/products', methods=['GET'])
def get_products():
products = Product.query.all()
return jsonify(products_schema.dump(products))
@api.route('/products', methods=['POST'])
def create_product():
data = request.get_json()
try:
product_data = product_schema.load(data)
except ValidationError as err:
return jsonify(err.messages), 400
product = Product(**product_data)
db.session.add(product)
db.session.commit()
return jsonify(product_schema.dump(product)), 201
@api.route('/products/<int:product_id>', methods=['PUT'])
def update_product(product_id):
product = Product.query.get_or_404(product_id)
data = request.get_json()
try:
product_data = product_schema.load(data, partial=True)
except ValidationError as err:
return jsonify(err.messages), 400
for key, value in product_data.items():
setattr(product, key, value)
db.session.commit()
return jsonify(product_schema.dump(product))
@api.route('/products/<int:product_id>', methods=['DELETE'])
def delete_product(product_id):
product = Product.query.get_or_404(product_id)
db.session.delete(product)
db.session.commit()
return '', 204
A task management application with user roles, task assignment, and real-time updates.
- User authentication with roles (Admin, Manager, Employee)
- Task creation, assignment, and tracking
- Real-time notifications using WebSocket
- File attachments for tasks
- Task comments and activity log
- Dashboard with task analytics
- User Roles and Permissions
from enum import Enum
from functools import wraps
from flask_login import current_user
from flask import abort
class UserRole(Enum):
ADMIN = 'admin'
MANAGER = 'manager'
EMPLOYEE = 'employee'
def role_required(role):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if not current_user.has_role(role):
abort(403)
return f(*args, **kwargs)
return decorated_function
return decorator
class User(UserMixin, db.Model):
# ... other fields ...
role = db.Column(db.Enum(UserRole), nullable=False, default=UserRole.EMPLOYEE)
def has_role(self, role):
return self.role == role
- Task Management
class Task(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
description = db.Column(db.Text)
due_date = db.Column(db.DateTime)
status = db.Column(db.String(20), default='pending')
priority = db.Column(db.String(20))
assigned_to_id = db.Column(db.Integer, db.ForeignKey('user.id'))
created_by_id = db.Column(db.Integer, db.ForeignKey('user.id'))
created_at = db.Column(db.DateTime, default=datetime.utcnow)
attachments = db.relationship('TaskAttachment', backref='task', lazy=True)
comments = db.relationship('TaskComment', backref='task', lazy=True)
activities = db.relationship('TaskActivity', backref='task', lazy=True)
class TaskActivity(db.Model):
id = db.Column(db.Integer, primary_key=True)
task_id = db.Column(db.Integer, db.ForeignKey('task.id'))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
action = db.Column(db.String(50))
details = db.Column(db.Text)
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
- Real-time Notifications using WebSocket
from flask_socketio import SocketIO, emit
from app import socketio
@socketio.on('connect')
def handle_connect():
if current_user.is_authenticated:
join_room(f'user_{current_user.id}')
join_room(f'role_{current_user.role.value}')
def notify_task_update(task_id, action, data):
task = Task.query.get(task_id)
if task:
# Notify assigned user
emit('task_update', {
'action': action,
'task_id': task_id,
'data': data
}, room=f'user_{task.assigned_to_id}')
# Notify managers
emit('task_update', {
'action': action,
'task_id': task_id,
'data': data
}, room=f'role_{UserRole.MANAGER.value}')
- Dashboard Analytics
@app.route('/dashboard')
@login_required
def dashboard():
# Task statistics
total_tasks = Task.query.count()
pending_tasks = Task.query.filter_by(status='pending').count()
completed_tasks = Task.query.filter_by(status='completed').count()
# Tasks by priority
priority_stats = db.session.query(
Task.priority,
db.func.count(Task.id).label('count'))