- 👤 Andrés Cruz

🇪🇸 En español

The Master Guide to Flask: Web Development with Python from Start to Finish

In the vast universe of Python web development, Flask shines as a beacon of flexibility and power. Unlike larger, more "opinionated" frameworks, Flask is a microframework that gives you total control, providing a solid, minimalist core upon which you can build anything, from a simple blog to a complex RESTful API or a real-time application. At DesarrolloLibre, we've explored every facet of Flask, and this guide is the distillation of years of practical experience.

This SUPER post is a complete manual that will walk you through every stage of the Flask development lifecycle. We'll start with setting up your environment and your first "Hello, World" application. Then, we'll delve into the framework's pillars: routing, the Jinja2 templating engine, configuration management, and environment variables. We will build a robust application with database handling, user authentication, role-based access control, secure forms, and flash notifications. Finally, we'll explore the advanced Flask ecosystem with asynchronous tasks, WebSockets, caching, testing, and show you how to deploy your creation to the cloud.

Get ready to master Flask, a tool that embodies the simplicity and power of Python.

Flask is a great web framework for creating server-side web applications with Python; If you do not have knowledge of using server web frameworks with Python, Flask is a good candidate to start with; Being a microframework, which means that initially it comes with the minimum necessary to be able to function, you can see little by little how the framework works without giving rise to confusion with files, directory structure or modules that you have not placed, as happens with others. more robust frameworks like Django, which when creating a project in Django, has many files and folders.

Although Flask is a microframework does not mean that it should be used for small projects, since it has a large number of extensions that you can configure for the project based on its needs; for example, if you need to send emails, there are already extensions, as well as if you want to log in socially, connect via ftp, user modules, etc.

Flask is a microframework used for the development of web applications in Python, although the term microframework does not confuse you since you can use it to build all kinds of web applications, both small and large. Some of its main features are:

Flask main features

Flask is a great microframework to start developing web applications with Python. Being a microframework, it provides the bare minimum to work without worrying about a structure that is difficult for beginners to follow, as is the case with Django, which makes it an excellent candidate to start with. in this little world.

Among the main features of Flask we have:

These are just some of the main features of Flask, but there are many others.

When installing Flask, several dependencies are installed, among the main ones we have:

Section 1: Fundamentals and Initial Setup

Before we can build complex applications, we must lay a solid foundation. This section will guide you through the installation of Flask, configuring your development environment in Visual Studio Code, and understanding essential concepts like environment variables and configuration files, which are crucial for creating maintainable and secure applications.

Preparing the Environment for Flask Development

If you're taking your first steps in Python web development, Flask is the perfect starting point. Its microframework nature makes it lightweight, flexible, and extremely powerful.

What is Flask?

Flask is a microframework for Python based on Werkzeug (a WSGI utility library) and Jinja2 (a templating engine). The term "micro" doesn't mean it's limited; it means its core is small and doesn't impose decisions on which tools to use. It gives you the freedom to choose your own libraries for database access (ORM), form validation, etc.

In my experience, this flexibility is its greatest strength. Unlike monolithic frameworks, Flask allows you to build the application piece by piece, understanding every component you add.

Installation and Virtual Environment

The first rule of Python development is: always use a virtual environment. This isolates your project's dependencies and avoids conflicts.

# 1. Create and navigate to your project folder 
mkdir my-flask-app && cd my-flask-app 
# 2. Create a virtual environment 
python -m venv venv 
# 3. Activate the virtual environment 
# Windows 
.\venv\Scripts\activate 
# macOS/Linux source venv/bin/activate 
# 4. Install Flask 
pip install Flask

Configuration in Visual Studio Code

VS Code is an excellent editor for Flask development. For an optimal experience:

  1. Install the official Python extension from Microsoft.
  2. Select your virtual environment's interpreter: Press Ctrl+Shift+P, search for "Python: Select Interpreter," and choose the one inside your venv folder.

With this, your environment is ready for the linter, debugger, and autocompletion to work correctly with your project's dependencies.

Follow the complete setup guide at: Prepare the environment to start developing our applications in Flask.

"Hello World" in Flask: Your First Web Application

Once the environment is configured, creating a functional web application in Flask requires only a few lines of code.

Creating the Main File

Create a file, for example run.py, with the following content:

from flask import Flask

# 1. Create an instance of the Flask application
# __name__ le dice a Flask dónde buscar recursos como plantillas y archivos estáticos.
app = Flask(__name__)

# 2. Define a route using a decorator
# This links the root URL ("/") to the home()
@app.route('/')
def home():
    # 3. The view function returns the response that will be sent to the browser.
    return "¡Hola, Mundo desde Flask!"

# 4. (Optional, for development) Start the server if the script is run directly.
if __name__ == '__main__':
    app.run(debug=True)

Running the Application

You can run the application in two ways:

  1. Directly with Python: python run.py. The if __name__ == '__main__': will take care of starting the server.
  2. With the flask command: First, you need to tell Flask where your application is via environment variables, and then you can use the flask run command.

The debug=True mode is incredibly useful during development, as it automatically restarts the server with every change and provides an interactive debugger in the browser if an error occurs.

Create your first app in minutes with our guide: Hello World Flask: create your first web app with Python.

Environment Variables: .flaskenv

Environment variables are crucial for configuring Flask's behavior without modifying the code. The two most important ones are:

Instead of setting them manually in the terminal every time, Flask can load them automatically from .env or .flaskenv files if you have python-dotenv installed.

$ pip install python-dotenv

Now, create a file named .flaskenv in your project's root:

# .flaskenv 
$ FLASK_APP=run.py 
$ FLASK_ENV=development

With this file, you only need to run flask run in your terminal, and Flask will know what to do. For sensitive variables (like API keys), use a .env file, which is also loaded automatically but should not be uploaded to your Git repository.

Master environment variables in: Create environment variables in Flask .flaskenv.

Configuration Files for a Scalable App

As your application grows, you'll need to manage more configuration parameters: the secret key, the database URI, mail settings, etc. Flask allows you to load configurations from an object, a class, or a file.

A recommended practice is to create a config.py file with different classes for each environment:

# config.py
class Config:
    SECRET_KEY = 'una-clave-secreta-muy-segura'

class DevConfig(Config):
    DEBUG = True
    SQLALCHEMY_DATABASE_URI = 'sqlite:///dev.db'

class ProdConfig(Config):
    DEBUG = False
    SQLALCHEMY_DATABASE_URI = 'postgresql://user:pass@host/db'

Then, in your application's main file, you can load the desired configuration:

from flask import Flask
import config

app = Flask(__name__)
app.config.from_object(config.DevConfig) # Load the development configuration

This allows you to easily switch between configurations for development, testing, and production without touching the application code. The SECRET_KEY is especially important, as Flask and its extensions use it to cryptographically sign session cookies and other security data.

Learn to structure your configurations in: Managing configuration parameters in Flask.

Section 2: Building the Web Application

With the fundamentals in place, it's time to build the main features of our web application. This section covers Flask's routing system to define our application's URLs, the use of the Jinja2 templating engine for rendering dynamic HTML, and handling web forms safely and efficiently with the WTForms extension.

Routes and Views: The Heart of a Flask Application

In Flask, a "route" is the association between a URL and a Python "view function." The @app.route() decorator is responsible for creating this association.

Dynamic Routes

Often, a URL needs to accept a variable part. For example, to display a product's profile by its ID. Flask handles this elegantly by specifying the variable type in the route.

@app.route('/producto/<int:id>')
def ver_producto(id):
    # The 'id' variable has already been converted to an integer. return f"Displaying product with ID: {id}"
    # Here we would look up the product in the database with that id. 
    return f"Mostrando el producto con ID: {id}"

You can use converters like string (default), int, float, and path.

HTTP Methods

By default, a route only responds to GET requests. To handle other methods like POST (for submitting forms), you must specify them in the decorator.

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        # Process the login form data
        return "Procesando login..."
    else:
        # Display the login form 
        return "This is the login form."

In Flask 2.0 and later, you can use more readable shortcut decorators: @app.get('/path') and @app.post('/path').

Master the routing system in: Routes in Flask.

Templating Engine with Jinja2

You'll rarely want to return plain text. Flask integrates with the Jinja2 templating engine to render dynamic HTML.

To use it, you need a folder named templates in the root of your project. The render_template() function will look for HTML files there.

@app.route('/saludo/<string:nombre>')
def saludo(nombre):
    # We pass the 'name' variable to the template context.
    return render_template('saludo.html', nombre=nombre)

Inside templates/greeting.html, you can use Jinja2 syntax:

<h1>Hello, {{ name }}!</h1>

Control Structures and Template Inheritance

Jinja2 is a complete templating engine. It supports loops, conditionals, and, most importantly, template inheritance. This allows you to create a base template (base.html) with the common structure (head, navbar, footer) and have other templates extend it.

<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}{% endblock %}</title>
</head>
<body>
    <div id="content">
        {% block content %}{% endblock %}
    </div>
</body>
</html>

Additionally, you can create macros to generate reusable HTML fragments, such as a pagination component.

Learn everything about Jinja2 in: Jinja2 in Flask 3 and create a pagination macro.

Secure and Validated Forms with WTForms

Handling HTML forms directly is tedious and error-prone. The Flask-WTF extension integrates with the WTForms library to provide a high-level solution for:

The process involves defining a form class that inherits from FlaskForm.

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')

In your view function, you create an instance of the form and handle the validation.

@app.route('/login', methods=['GET', 'POST'])
def login():
    # 1. Instantiate the form object
    form = LoginForm()

    # 2. Handle form submission (POST request) and validation
    if form.validate_on_submit():
        # The data is valid. Process the login.
        # form.email.data and form.password.data contain the submitted values.
        
        # Display a success message (flash message)
        flash(f'Login requested for user {form.email.data}')
        
        # Redirect the user to the home page (Post/Redirect/Get pattern)
        return redirect(url_for('home'))

    # 3. Handle initial page load (GET) or validation failure (POST)
    # Render the login template and pass the form object to it.
    return render_template('login.html', form=form)

In the template, you can render the fields, including labels and error messages. Flask-WTF also takes care of adding a hidden CSRF token for security.

Build robust forms with: WTForms in Flask 3.

Section 3: Authentication and User Management

Almost all web applications need a way to identify their users. This section will guide you through implementing a complete authentication and authorization system in Flask, from managing session data and user authentication to creating a role-based access control system.

Session in Flask: Storing User Data

HTTP is a stateless protocol, meaning each request is independent. To remember who a user is between requests, we need a session mechanism. Flask provides a session object that works like a dictionary. You can store data in it, and it will be available in subsequent requests from the same user.

By default, Flask uses secure cookies (cryptographically signed) to store session data on the client side. That's why it's crucial to set a strong SECRET_KEY in your configuration.

from flask import session, redirect, url_for

@app.route('/set_user/<name>')
def set_user(name):
    # Store the 'name' captured from the URL route into the session dictionary
    session['username'] = name
    return 'User saved to session.'

The session is the foundation upon which we will build our authentication system.

Understand how the session works in: Session in Flask: how to store and manage user data.

User Authentication with Flask-Login

The Flask-Login extension greatly simplifies user session management. It handles logging users in and out, remembering users between sessions, and protecting views so that only authenticated users can access them.

Flask-Login Setup

The process requires several steps:

  1. Initialize the extension: login_manager = LoginManager(app)
  2. Create a User Model: Your user model (e.g., from SQLAlchemy) must inherit from UserMixin, which provides the default implementations that Flask-Login needs.
  3. Create a user_loader: You must provide a function decorated with @login_manager.user_loader that tells Flask-Login how to load a user from their ID, which is stored in the session.
from flask_login import UserMixin, login_user, logout_user, login_required, current_user class User(UserMixin, db.Model): # ... your fields ... @login_manager.user_loader def load_user(user_id): return User.query.get(int(user_id))

Logging In and Out

In your login view, after validating the user's credentials, you call login_user().

from flask_login import UserMixin, login_user, logout_user, login_required, current_user

class User(UserMixin, db.Model):
    # ... fields ...

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))

To log out, you simply call logout_user().

View Protection and current_user

To protect a route, you just need to add the @login_required decorator. If an unauthenticated user tries to access it, Flask-Login will redirect them to the login page.

Inside any view, you can access the currently authenticated user through the current_user proxy.

from flask_login import login_required, current_user @app.route('/dashboard') @login_required def dashboard(): return f"Welcome to your dashboard, {current_user.username}!"

Implement a complete login system with: How to implement user authentication with Flask-Login.

Role-Based Access Control (RBAC)

Authentication answers the question "who are you?", while authorization answers "what are you allowed to do?". A role system is the most common way to implement authorization.

The process involves extending your database to include Role models and an association table to handle a many-to-many relationship between users and roles.

@app.route('/login', methods=['GET', 'POST'])
def login():
    # ... validations ...
    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('dashboard'))
    # ...

Creating a Permission Decorator

To protect routes for specific roles (e.g., 'admin'), we can create our own decorator.

from functools import wraps
from flask import abort

def role_required(role_name):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            if not current_user.is_authenticated or not current_user.has_role(role_name):
                abort(403) # Forbidden
            return f(*args, **kwargs)
        return decorated_function
    return decorator

@app.route('/admin')
@login_required
@role_required('admin')
def admin_dashboard():
  return "Welcome to the administrator panel."

This system gives you granular control over who can access which parts of your application.

Add a role and permissions system with: How to implement Role-Based Access Control in Flask.

Section 4: Advanced Topics and Flask Ecosystem

Once you master the fundamentals, the Flask ecosystem opens up a world of possibilities for building more powerful, interactive, and efficient applications. In this section, we'll explore how to create RESTful APIs, enable real-time communication with WebSockets, optimize performance with background tasks and caching, and ensure your code quality with automated tests.

Creating RESTful APIs

Flask is an exceptional tool for building RESTful APIs thanks to its simplicity. A REST API allows different systems (like a mobile application or a JavaScript frontend) to communicate with your backend via HTTP.

The principle is the same as with web views, but instead of returning HTML with render_template, you return data in JSON format using the jsonify function.

from flask import jsonify

@app.route('/api/tasks', methods=['GET'])
def get_tasks():
    tasks = Task.query.all()
    return jsonify([task.to_dict() for task in tasks])

@app.route('/api/tasks', methods=['POST'])
def create_task():
    # ... lógica para crear la tarea a partir de request.json ...
    return jsonify(new_task.to_dict()), 201 # 201 Created

Enabling CORS for Your API

If your API will be consumed by a frontend that lives on a different domain, you'll encounter CORS (Cross-Origin Resource Sharing) errors. For security reasons, the browser blocks these requests. The Flask-CORS extension solves this by adding the necessary HTTP headers.

$ pip install Flask-Cors
from flask_cors import CORS

app = Flask(__name__)
# Enable CORS for all routes and origins CORS(app) 
CORS(app) 

For production, you can configure it to allow only specific origins.

Build your first API in: Create a Restful API in Flask and enable CORS.

Asynchronous Background Tasks with Celery

Some tasks take too long to run within an HTTP request, such as sending an email, processing an image, or generating a report. If you do it in the view, the user will have to wait until the task finishes, resulting in a poor experience.

Celery is a distributed task queue that allows you to execute these tasks asynchronously in a separate process ("worker").

The flow is:

  1. A Flask view receives a request.
  2. Instead of executing the heavy task directly, it "sends" it to the Celery queue with .delay().
  3. The view returns an immediate response to the user.
  4. A Celery "worker" process, running in the background, picks up the task from the queue and executes it.

This is indispensable for the scalability and responsiveness of your application.

Real-Time Communication with Flask-SocketIO

For applications that require instant, bidirectional communication, such as chats or real-time notifications, the HTTP request-response model is not enough. This is where WebSockets come in.

The Flask-SocketIO extension integrates the Socket.IO protocol into your Flask application, allowing persistent, full-duplex communication between the client and the server.

You can define "events" and "rooms" to send messages to specific clients.

from flask_socketio import SocketIO, emit, join_room

app = Flask(__name__)
socketio = SocketIO(app)

@socketio.on('join')
def on_join(data):
    room = data['room']
    join_room(room)
    emit('status', {'msg': 'Joined.'}, to=room)

@socketio.on('chat_message')
def handle_message(data):
    room = data['room']
    emit('new_message', data['message'], to=room)

This allows you to build highly interactive applications without the client having to constantly "poll" the server for new data.

Create your first real-time application with: How to use Flask-SocketIO.

Unit Testing with Pytest

Writing automated tests is a fundamental practice to ensure your application works correctly and to prevent regressions. Pytest is a testing framework for Python that makes writing tests simple and readable.

Flask integrates well with Pytest. You can create a "fixture" to set up a test client that makes requests to your application without the need for a real web server.

# tests/conftest.py
import pytest
from my_app import create_app

@pytest.fixture
def client():
    # 1. Create a Flask application instance configured for testing
    app = create_app('testing')

    # 2. Use the application's test client for making requests
    with app.test_client() as client:
        # 3. Establish an application context
        with app.app_context():
            # Here you could initialize the test database (e.g., db.create_all())
            pass 
        
        # 4. Yield the client for the test function to use
        yield client

Tests give you the confidence to refactor and add new features without fear of breaking what already works.

Learn to configure your test suite in: Unit Tests in Flask with Pytest.

Section 5: Deployment

Once your application is built and tested, the final step is to put it online for the world to use. Deployment can seem intimidating, but modern platforms like Railway have greatly simplified it, allowing you to go from code on your machine to a live application in a matter of minutes.

Deploying a Flask Project on Railway

Railway is a cloud infrastructure platform that automates much of the deployment process. It works by connecting to your GitHub repository and deploying your application every time you push to your main branch.

Preparing the Project for Deployment

Before deploying, you need three key files in your repository:

  1. requirements.txt: A list of all your project's Python dependencies. Generated with pip freeze > requirements.txt. Railway will use it to install everything needed.
  2. Procfile: A text file without an extension that tells Railway how to run your application. For a Flask application, you need a production WSGI server like Gunicorn.
# Procfile web: gunicorn --bind 0.0.0.0:$PORT run:app

(Make sure you have gunicorn in your requirements.txt).

  1. runtime.txt (Optional but recommended): Specifies the Python version to use.
# runtime.txt python-3.11.4

Railway Deployment Process

  1. Create an account on Railway and connect your GitHub.
  2. Create a New Project: Select "Deploy from GitHub repo" and choose your Flask repository.
  3. Provision a Database: If your application needs a database, you can add a PostgreSQL or MySQL service with a single click from the Railway panel.
  4. Configure Environment Variables: Railway will detect your database variables and inject them into your application. You must manually add any other variables you need, such as your SECRET_KEY or third-party API keys.
  5. Deploy! Railway will detect your Procfile and requirements.txt, build your application, and deploy it. In minutes, it will provide you with a public URL where your application will be live.

Platforms like Railway democratize deployment, allowing developers to focus on code and not on the complexity of infrastructure.

Follow our step-by-step guide to deploy your application in: Deploy a Flask or FastAPI project on Railway.

Course and Book to Master Flask 3

Remember that on this blog, you have access to COMPLETE courses and books to help you master Flask. All the posts mentioned above are taken from the course and book; you can purchase them from the Courses and Books section in the menu.

Conclusion: The Power of Flask's Simplicity

Throughout this guide, we have journeyed from the most basic concepts of Flask to the construction and deployment of a complete and robust web application. We have seen how Flask's microframework philosophy, far from being a limitation, is its greatest strength. It grants you the freedom to choose your tools, structure your project as you wish, and build exactly the application you need, without the burden of unnecessary components.

Ver Listado »