Table of Contents
- Introduction
- Setting Up Your Environment
- 2.1 Installing Python
- 2.2 Virtual Environments
- 2.3 Installing Flask
- Your First Flask App: “Hello World”
- 3.1 Project Structure
- 3.2 Writing the Code
- 3.3 Running the App
- Routing and URL Handling
- 4.1 Basic Routes
- 4.2 Dynamic URLs
- 4.3 HTTP Methods (GET, POST)
- Templates with Jinja2
- 5.1 Template Inheritance
- 5.2 Variables, Loops, and Conditionals
- Serving Static Files (CSS, JS, Images)
- Handling Forms with Flask-WTF
- 7.1 Installing Flask-WTF
- 7.2 Creating a Form
- 7.3 Rendering and Validating Forms
- Database Integration with SQLAlchemy
- 8.1 Installing Flask-SQLAlchemy
- 8.2 Defining Models
- 8.3 CRUD Operations
- User Authentication with Flask-Login
- 9.1 Setting Up Flask-Login
- 9.2 User Registration and Login
- 9.3 Protecting Routes
- Deploying Your Flask App
- 10.1 Preparing for Deployment
- 10.2 Deploying to Heroku
- Best Practices for Flask Development
- Conclusion
- References
2. Setting Up Your Environment
Before diving into Flask, let’s set up a clean development environment. This ensures dependencies are isolated and your project remains reproducible.
2.1 Installing Python
Flask requires Python 3.6 or higher. If you don’t have Python installed:
- Windows/macOS: Download from python.org.
- Linux: Use your package manager (e.g.,
sudo apt install python3for Ubuntu).
Verify installation by running:
python --version # or python3 --version
2.2 Virtual Environments
A virtual environment isolates project dependencies. Use Python’s built-in venv module:
# Create a project folder
mkdir flask-webapp && cd flask-webapp
# Create a virtual environment
python -m venv venv
# Activate the virtual environment
# On Windows (Command Prompt):
venv\Scripts\activate
# On Windows (PowerShell):
.\venv\Scripts\Activate.ps1
# On macOS/Linux:
source venv/bin/activate
You’ll see (venv) in your terminal prompt, indicating the environment is active.
2.3 Installing Flask
With the virtual environment active, install Flask using pip:
pip install flask
Verify installation by running:
flask --version
You should see output like Flask 2.3.3, Python 3.9.7.
3. Your First Flask App: “Hello World”
Let’s build a minimal Flask app to test our setup.
3.1 Project Structure
For now, our project will have a single file:
flask-webapp/
├── venv/
└── app.py # Our Flask application
3.2 Writing the Code
Create app.py with the following content:
# app.py
from flask import Flask
# Initialize the Flask app
app = Flask(__name__)
# Define a route for the homepage
@app.route('/')
def hello_world():
return "Hello, Flask!"
# Run the app if this file is executed directly
if __name__ == '__main__':
app.run(debug=True)
3.3 Running the App
In the terminal (with venv active), run:
python app.py
You’ll see output like:
* Serving Flask app 'app'
* Debug mode: on
* Running on http://127.0.0.1:5000 (Press CTRL+C to quit)
Open http://localhost:5000 in your browser. You should see “Hello, Flask!”—congratulations, your first Flask app is running!
Note:
debug=Trueenables auto-reloading when you edit code (use only in development, not production).
4. Routing and URL Handling
Routes map URLs to Python functions (called “view functions”). Flask uses the @app.route() decorator to define routes.
4.1 Basic Routes
Add more routes to app.py to handle different URLs:
# app.py (updated)
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return "Welcome to the Home Page!"
@app.route('/about')
def about():
return "This is the About Page."
@app.route('/contact')
def contact():
return "Contact us at [email protected]"
if __name__ == '__main__':
app.run(debug=True)
Now visit http://localhost:5000/about or /contact to see the new pages.
4.2 Dynamic URLs
Flask supports dynamic URL parameters using <variable_name>. For example, a user profile page:
@app.route('/user/<username>')
def user_profile(username):
return f"Hello, {username}!"
Visit http://localhost:5000/user/Alice—you’ll see “Hello, Alice!“.
You can specify data types (e.g., int, float):
@app.route('/post/<int:post_id>')
def show_post(post_id):
return f"Displaying Post #{post_id}"
Now http://localhost:5000/post/42 works, but /post/abc returns a 404 error.
4.3 HTTP Methods
By default, routes accept only GET requests. To handle POST (e.g., form submissions), specify methods in @app.route():
from flask import request # Add this import
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
# Handle form submission
username = request.form['username']
return f"Logged in as {username} (via POST)"
else:
# Show login form (GET request)
return "Please submit the login form (via GET)"
Test with curl -X POST -d "username=Alice" http://localhost:5000/login (or use a form later).
5. Templates with Jinja2
Flask uses Jinja2, a powerful templating engine, to render dynamic HTML. Templates let you separate logic (Python) from presentation (HTML).
5.1 Project Structure for Templates
Create a templates folder to store HTML files:
flask-webapp/
├── venv/
├── app.py
└── templates/
├── base.html # Base template (shared across pages)
├── index.html # Homepage
└── about.html # About page
5.2 Template Inheritance
Avoid repeating HTML (e.g., headers, footers) with template inheritance. Define a base.html with common elements, then extend it in other templates.
templates/base.html:
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}My Flask App{% endblock %}</title>
</head>
<body>
<header>
<h1>My Flask App</h1>
<nav>
<a href="/">Home</a> |
<a href="/about">About</a>
</nav>
</header>
<main>
{% block content %}{% endblock %} <!-- Page-specific content here -->
</main>
<footer>
<p>© 2024 My Flask App</p>
</footer>
</body>
</html>
templates/index.html (extends base.html):
{% extends "base.html" %}
{% block title %}Home{% endblock %}
{% block content %}
<h2>Welcome to the Home Page</h2>
<p>Today's date: {{ today }}</p> <!-- Dynamic variable -->
{% endblock %}
5.3 Rendering Templates
Use render_template() to send templates to the browser. Update app.py:
from flask import render_template # Add this import
from datetime import datetime # For dynamic date
@app.route('/')
def home():
today = datetime.now().strftime("%Y-%m-%d")
return render_template('index.html', today=today) # Pass variables to template
@app.route('/about')
def about():
return render_template('about.html')
templates/about.html:
{% extends "base.html" %}
{% block title %}About{% endblock %}
{% block content %}
<h2>About Us</h2>
<p>This is a Flask web app built with Python.</p>
{% endblock %}
Visit http://localhost:5000—you’ll see the dynamic date and shared header/footer!
5.4 Jinja2 Features
Jinja2 supports variables, loops, conditionals, and more:
- Variables:
{{ user.name }}(passed from Flask) - Loops:
<ul> {% for post in posts %} <li>{{ post.title }}</li> {% endfor %} </ul> - Conditionals:
{% if user.is_authenticated %} <p>Welcome back, {{ user.username }}!</p> {% else %} <p>Please log in.</p> {% endif %}
6. Serving Static Files (CSS, JS, Images)
Static files (CSS, JavaScript, images) enhance your app’s design and interactivity. Flask serves them from a static folder.
6.1 Project Structure
Add a static folder with subfolders for assets:
flask-webapp/
├── ...
├── static/
│ ├── css/
│ │ └── style.css
│ ├── js/
│ │ └── app.js
│ └── img/
│ └── logo.png
└── templates/
...
6.2 Linking Static Files in Templates
Use url_for('static', filename='path/to/file') to link static files in Jinja2:
templates/base.html (updated head section):
<head>
<title>{% block title %}My Flask App{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
<script src="{{ url_for('static', filename='js/app.js') }}"></script>
</head>
6.3 Example CSS
static/css/style.css:
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
header {
background-color: #f0f0f0;
padding: 10px;
border-radius: 5px;
}
nav a {
margin-right: 15px;
text-decoration: none;
color: #333;
}
nav a:hover {
color: #007bff;
}
Refresh http://localhost:5000—your app now has styling!
7. Handling Forms with Flask-WTF
Forms are critical for user input (e.g., login, registration). Flask-WTF simplifies form handling, including validation and CSRF protection.
7.1 Installing Flask-WTF
pip install flask-wtf
7.2 Creating a Form
Define forms as Python classes. Create forms.py in your project:
# forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Email, Length
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
password = PasswordField('Password', validators=[DataRequired(), Length(min=6)])
submit = SubmitField('Log In')
class RegistrationForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Email()])
username = StringField('Username', validators=[DataRequired(), Length(min=4, max=20)])
password = PasswordField('Password', validators=[DataRequired(), Length(min=6)])
submit = SubmitField('Register')
validatorsensure inputs are valid (e.g.,DataRequired()= field can’t be empty).
7.3 Rendering and Validating Forms
Update app.py to handle the login form:
# app.py (updated)
from flask import Flask, render_template, redirect, url_for, flash
from forms import LoginForm # Import the form
import secrets
app = Flask(__name__)
app.config['SECRET_KEY'] = secrets.token_hex(16) # Required for CSRF protection
@app.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit(): # Checks if form is submitted and valid
# Validate credentials (replace with real logic later)
if form.username.data == 'admin' and form.password.data == 'password':
flash('Logged in successfully!', 'success') # Show message to user
return redirect(url_for('home')) # Redirect to homepage
else:
flash('Invalid username or password', 'danger')
return render_template('login.html', form=form)
7.4 Creating a Login Template
templates/login.html:
{% extends "base.html" %}
{% block title %}Login{% endblock %}
{% block content %}
<h2>Login</h2>
<form method="POST">
{{ form.hidden_tag() }} <!-- Required for CSRF protection -->
<div>
{{ form.username.label }}<br>
{{ form.username(size=32) }}<br>
{% for error in form.username.errors %}
<span style="color: red;">{{ error }}</span>
{% endfor %}
</div>
<div>
{{ form.password.label }}<br>
{{ form.password(size=32) }}<br>
{% for error in form.password.errors %}
<span style="color: red;">{{ error }}</span>
{% endfor %}
</div>
<div>{{ form.submit() }}</div>
</form>
<!-- Display flash messages -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div style="color: {% if category == 'success' %}green{% else %}red{% endif %}">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
{% endblock %}
Add a link to /login in base.html’s nav:
<a href="{{ url_for('login') }}">Login</a>
Test with username “admin” and password “password”—you’ll see a success message!
8. Database Integration with SQLAlchemy
Most apps need a database to store data (users, posts, etc.). Flask-SQLAlchemy is an ORM (Object-Relational Mapper) that lets you interact with databases using Python objects instead of raw SQL.
8.1 Installing Flask-SQLAlchemy
pip install flask-sqlalchemy
8.2 Setting Up the Database
Configure the database in app.py:
# app.py (updated)
from flask_sqlalchemy import SQLAlchemy
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db' # SQLite database file
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # Disable deprecated feature
db = SQLAlchemy(app) # Initialize SQLAlchemy
8.3 Defining Models
Models are Python classes that represent database tables. Create a models.py file:
# models.py
from datetime import datetime
from app import db, login_manager
from flask_login import UserMixin
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False) # Store hashed passwords!
posts = db.relationship('Post', backref='author', lazy=True)
def __repr__(self):
return f"User('{self.username}', '{self.email}')"
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
content = db.Column(db.Text, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
def __repr__(self):
return f"Post('{self.title}', '{self.date_posted}')"
UserandPostare tables;id,username, etc., are columns.posts = db.relationship('Post', ...)links users to their posts (one-to-many).
8.4 Creating the Database
In the Python shell, create tables:
python
>>> from app import app, db
>>> app.app_context().push() # Required for Flask-SQLAlchemy >= 2.0
>>> db.create_all() # Creates tables based on models
A site.db file will appear in your project folder.
8.5 CRUD Operations
Add, read, update, or delete data using SQLAlchemy:
# Add a user
new_user = User(username='Alice', email='[email protected]', password='hashed_password')
db.session.add(new_user)
db.session.commit()
# Query users
user = User.query.filter_by(username='Alice').first()
all_users = User.query.all()
# Update a user
user.username = 'Alicia'
db.session.commit()
# Delete a user
db.session.delete(user)
db.session.commit()
9. User Authentication with Flask-Login
Flask-Login handles user sessions, login/logout, and route protection.
9.1 Installing Flask-Login
pip install flask-login
9.2 Setting Up Flask-Login
Update app.py to initialize Flask-Login:
# app.py (updated)
from flask_login import LoginManager, login_user, logout_user, login_required, current_user
login_manager = LoginManager(app)
login_manager.login_view = 'login' # Redirect here if login is required
login_manager.login_message_category = 'info'
9.3 Protecting Routes
Use the @login_required decorator to restrict access:
@app.route('/dashboard')
@login_required
def dashboard():
return f"Welcome to your dashboard, {current_user.username}!"
9.4 Login/Logout Logic
Update the login route to use login_user:
from werkzeug.security import check_password_hash # For password hashing (see best practices)
@app.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('home')) # Redirect if already logged in
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user and check_password_hash(user.password, form.password.data): # Check hashed password
login_user(user)
return redirect(url_for('dashboard'))
else:
flash('Invalid username or password', 'danger')
return render_template('login.html', form=form)
@app.route('/logout')
def logout():
logout_user()
return redirect(url_for('home'))
10. Deploying Your Flask App
Once your app is ready, deploy it to a production server. We’ll use Heroku (free tier available).
10.1 Preparing for Deployment
-
Create
requirements.txt(lists dependencies):pip freeze > requirements.txt -
Create a
Procfile(tells Heroku how to run the app):web: gunicorn app:app -
Install Gunicorn (WSGI server for production):
pip install gunicorn
10.2 Deploying to Heroku
-
Sign up at Heroku.com and install the Heroku CLI.
-
Login and deploy:
heroku login heroku create my-flask-app # Replace with your app name git init git add . git commit -m "Initial commit" git push heroku main heroku open # Opens your app in the browser
11. Best Practices for Flask Development
- Use Virtual Environments: Isolate dependencies.
- Store Secrets Securely: Use environment variables (e.g.,
python-dotenv) instead of hardcodingSECRET_KEY. - Hash Passwords: Use
werkzeug.security.generate_password_hashandcheck_password_hash. - Validate Input: Always validate user input to prevent attacks.
- Use Blueprints: Organize large apps into blueprints (e.g.,
auth/,blog/). - Test: Use
pytestto write tests for your app.
12. Conclusion
Flask’s flexibility and simplicity make it an excellent choice for building web applications with Python. From “Hello World” to full-featured apps with authentication and databases, Flask scales with your needs. By combining Flask with extensions like Flask-SQLAlchemy and Flask-Login, you can build robust, production-ready apps with minimal overhead.
Now it’s your turn—start building!
13. References
- Flask Official Documentation
- Flask-SQLAlchemy Documentation
- Flask-Login Documentation
- Jinja2 Template Designer Documentation
- Heroku Flask Deployment Guide
- “Flask Web Development with Python” by Miguel Grinberg (book)