Flask: Criando Aplicações Web Leves em Python
Flask é um microframework web para Python: ele fornece o estritamente necessário para construir uma aplicação web (roteamento, templates, tratamento de requisições) e deixa você escolher o resto (banco de dados, autenticação, formulários). Essa filosofia minimalista faz dele um excelente ponto de partida para aprender desenvolvimento web em Python, sem deixar de ser poderoso o bastante para aplicações em produção. Este guia acompanha você da instalação até o deploy.
Instalação e aplicação mínima
Crie um ambiente virtual e depois instale o Flask:
# Création et activation de l'environnement virtuel
python -m venv venv
# Windows
venv\Scripts\activate
# macOS / Linux
source venv/bin/activate
# Installation
pip install flask
Uma aplicação Flask completa cabe em poucas linhas. Crie um arquivo app.py:
from flask import Flask
# Criação da aplicação
app = Flask(__name__)
# Associação de uma URL a uma função (uma "view")
@app.route('/')
def accueil():
return "Bonjour, Flask !"
if __name__ == '__main__':
# debug=True : recarregamento automático e mensagens de erro detalhadas
app.run(debug=True)
Inicie o servidor de desenvolvimento:
python app.py
# * Running on http://127.0.0.1:5000
Abra http://127.0.0.1:5000 no navegador: a página exibe “Bonjour, Flask !”. Nunca use debug=True em produção: o depurador integrado permite executar código arbitrário.
Rotas e métodos HTTP
O decorador @app.route associa uma URL a uma função. Por padrão, uma rota responde apenas a requisições GET; o parâmetro methods autoriza outros verbos HTTP:
from flask import Flask, request
app = Flask(__name__)
@app.route('/contact', methods=['GET', 'POST'])
def contact():
if request.method == 'POST':
# Tratamento do envio do formulário
return "Message reçu"
# Exibição do formulário (GET)
return "Formulaire de contact"
| Método | Uso típico |
|---|---|
| GET | Ler um recurso, exibir uma página |
| POST | Criar um recurso, enviar um formulário |
| PUT | Substituir um recurso existente |
| PATCH | Modificar parcialmente um recurso |
| DELETE | Excluir um recurso |
Parâmetros de URL
Os segmentos variáveis de uma URL são declarados entre sinais de menor e maior e passados como argumento para a função:
# Parâmetro string (padrão)
@app.route('/utilisateur/<nom>')
def profil(nom):
return f"Profil de {nom}"
# Parâmetro inteiro: /article/42 funciona, /article/abc retorna 404
@app.route('/article/<int:article_id>')
def article(article_id):
return f"Article numéro {article_id}"
# Parâmetro de caminho: aceita barras
@app.route('/fichiers/<path:chemin>')
def fichier(chemin):
return f"Chemin demandé : {chemin}"
Conversores disponíveis: string (padrão), int, float, path, uuid.
Os parâmetros de consulta (?tri=date&page=2) são lidos com request.args:
@app.route('/recherche')
def recherche():
terme = request.args.get('q', '') # valor padrão: ''
page = request.args.get('page', 1, type=int) # conversão automática
return f"Recherche de « {terme} », page {page}"
Templates Jinja2
Retornar HTML dentro de strings Python rapidamente se torna ilegível. O Flask integra o mecanismo de templates Jinja2: os arquivos HTML ficam em uma pasta templates/.
Estrutura do projeto:
mon_projet/
├── app.py
├── templates/
│ ├── base.html
│ └── index.html
└── static/
└── style.css
Herança de templates
O template base define a estrutura comum; as páginas preenchem os blocos:
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>{% block titre %}Mon Site{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<nav>
<a href="{{ url_for('accueil') }}">Accueil</a>
</nav>
<main>
{% block contenu %}{% endblock %}
</main>
</body>
</html>
<!-- templates/index.html -->
{% extends "base.html" %}
{% block titre %}Accueil - Mon Site{% endblock %}
{% block contenu %}
<h1>Bienvenue, {{ nom }} !</h1>
{% if articles %}
<ul>
{% for article in articles %}
<li>{{ article.titre }} ({{ article.date }})</li>
{% endfor %}
</ul>
{% else %}
<p>Aucun article pour le moment.</p>
{% endif %}
{% endblock %}
No lado Python, render_template carrega o template e transmite variáveis a ele:
from flask import render_template
@app.route('/')
def accueil():
articles = [
{"titre": "Premier article", "date": "2026-09-01"},
{"titre": "Deuxième article", "date": "2026-09-10"},
]
return render_template('index.html', nom="Alice", articles=articles)
O Jinja2 faz o escape automático do HTML das variáveis, o que protege contra ataques XSS.
Arquivos estáticos
Folhas de estilo, scripts e imagens ficam na pasta static/. A função url_for gera suas URLs:
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<img src="{{ url_for('static', filename='images/logo.png') }}" alt="Logo">
Use sempre url_for em vez de caminhos fixos no código: as URLs continuam corretas mesmo que a aplicação seja implantada sob um prefixo.
Formulários e o objeto request
Os dados de um formulário enviado via POST são lidos com request.form:
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/inscription', methods=['GET', 'POST'])
def inscription():
if request.method == 'POST':
nom = request.form.get('nom', '').strip()
email = request.form.get('email', '').strip()
# Validation minimale
if not nom or not email:
return render_template('inscription.html',
erreur="Tous les champs sont obligatoires")
# Aqui: gravação no banco de dados
return f"Bienvenue, {nom} !"
return render_template('inscription.html')
<!-- templates/inscription.html -->
{% extends "base.html" %}
{% block contenu %}
{% if erreur %}<p class="erreur">{{ erreur }}</p>{% endif %}
<form method="post">
<input type="text" name="nom" placeholder="Votre nom">
<input type="email" name="email" placeholder="Votre email">
<button type="submit">S'inscrire</button>
</form>
{% endblock %}
Em projetos reais, a biblioteca Flask-WTF adiciona validação e proteção contra CSRF.
Redirecionamentos e mensagens flash
Após um POST bem-sucedido, redirecione o usuário (padrão “Post/Redirect/Get”) e exiba uma mensagem temporária com flash:
from flask import Flask, flash, redirect, url_for, render_template
app = Flask(__name__)
app.secret_key = "changez-moi-en-production" # necessário para flash e sessões
@app.route('/enregistrer', methods=['POST'])
def enregistrer():
# ... tratamento ...
flash("Enregistrement réussi !", "succes")
return redirect(url_for('accueil'))
<!-- Dans base.html, avant le bloc contenu -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% for categorie, message in messages %}
<div class="flash flash-{{ categorie }}">{{ message }}</div>
{% endfor %}
{% endwith %}
API JSON com jsonify
O Flask é muito adequado para APIs REST. A função jsonify serializa um dicionário em uma resposta JSON com o cabeçalho Content-Type correto:
from flask import Flask, jsonify, request
app = Flask(__name__)
TACHES = [
{"id": 1, "titre": "Apprendre Flask", "faite": True},
{"id": 2, "titre": "Créer une API", "faite": False},
]
@app.route('/api/taches', methods=['GET'])
def lister_taches():
return jsonify(TACHES)
@app.route('/api/taches/<int:tache_id>', methods=['GET'])
def detail_tache(tache_id):
tache = next((t for t in TACHES if t["id"] == tache_id), None)
if tache is None:
return jsonify({"erreur": "Tâche introuvable"}), 404
return jsonify(tache)
@app.route('/api/taches', methods=['POST'])
def creer_tache():
donnees = request.get_json() # corpo JSON da requisição
if not donnees or "titre" not in donnees:
return jsonify({"erreur": "Le champ 'titre' est requis"}), 400
nouvelle = {"id": len(TACHES) + 1, "titre": donnees["titre"], "faite": False}
TACHES.append(nouvelle)
return jsonify(nouvelle), 201
Teste rápido com curl:
curl http://127.0.0.1:5000/api/taches
# [{"faite": true, "id": 1, "titre": "Apprendre Flask"}, ...]
curl -X POST http://127.0.0.1:5000/api/taches \
-H "Content-Type: application/json" \
-d '{"titre": "Déployer"}'
# {"faite": false, "id": 3, "titre": "Déployer"}
Estruturando com blueprints
Quando a aplicação cresce, um único arquivo se torna ingerenciável. Os blueprints agrupam as rotas por domínio funcional:
# blog/routes.py
from flask import Blueprint, render_template
blog_bp = Blueprint('blog', __name__, url_prefix='/blog')
@blog_bp.route('/')
def liste_articles():
return render_template('blog/liste.html')
@blog_bp.route('/<int:article_id>')
def detail(article_id):
return render_template('blog/detail.html', article_id=article_id)
# app.py
from flask import Flask
from blog.routes import blog_bp
app = Flask(__name__)
app.register_blueprint(blog_bp)
# As rotas /blog/ e /blog/<id> agora estão ativas
Estrutura típica de um projeto organizado:
mon_projet/
├── app.py
├── blog/
│ └── routes.py
├── auth/
│ └── routes.py
├── templates/
└── static/
Configuração e variáveis de ambiente
Nunca coloque segredos (chaves, senhas de banco de dados) no código. Carregue-os a partir do ambiente:
import os
from flask import Flask
app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev-uniquement')
app.config['DATABASE_URL'] = os.environ.get('DATABASE_URL')
app.config['DEBUG'] = os.environ.get('FLASK_DEBUG', '0') == '1'
Em desenvolvimento, o pacote python-dotenv carrega automaticamente um arquivo .env (que deve ser excluído do repositório Git via .gitignore):
# .env
SECRET_KEY=une-longue-chaine-aleatoire
DATABASE_URL=postgresql://user:pass@localhost/mabase
FLASK_DEBUG=1
Deploy: gunicorn e nginx
O servidor integrado do Flask é de processo único e não é seguro: ele é reservado ao desenvolvimento. Em produção, use um servidor WSGI como o Gunicorn atrás de um servidor web nginx:
pip install gunicorn
# 4 processos de trabalho, escutando na porta 8000
gunicorn --workers 4 --bind 127.0.0.1:8000 app:app
O nginx atua como proxy reverso: ele recebe o tráfego HTTPS, serve os arquivos estáticos diretamente e encaminha o restante para o Gunicorn:
server {
listen 80;
server_name exemple.com;
location /static/ {
alias /var/www/mon_projet/static/;
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Plataformas como Render, Railway ou Fly.io automatizam essa montagem se você não quiser gerenciar um servidor.
Flask vs Django
| Critério | Flask | Django |
|---|---|---|
| Filosofia | Microframework, minimalista | Framework completo, “baterias incluídas” |
| Curva de aprendizado | Suave, você aprende conforme a necessidade | Mais íngreme, muitos conceitos logo de início |
| ORM | À escolha (SQLAlchemy na maioria dos casos) | ORM do Django integrado |
| Administração | A construir ou via extensão | Interface de administração gerada automaticamente |
| Autenticação | Extensão (Flask-Login) | Integrada |
| Estrutura do projeto | Livre | Imposta e convencional |
| Ideal para | APIs, microsserviços, sites pequenos, protótipos | Aplicações completas com back-office |
Os dois frameworks são maduros e usados em produção em larga escala. Escolha o Flask pela leveza e flexibilidade, e o Django quando quiser o máximo de funcionalidades prontas para uso.
Boas práticas
- Use sempre um ambiente virtual e um arquivo
requirements.txt(pip freeze > requirements.txt). - Desative o modo debug em produção: é uma grave falha de segurança.
- Gere as URLs com
url_forem vez de escrevê-las diretamente no código. - Armazene os segredos em variáveis de ambiente, nunca no código-fonte.
- Estruture com blueprints assim que a aplicação ultrapassar algumas rotas.
- Retorne os códigos HTTP corretos nas suas APIs: 201 para uma criação, 400 para uma requisição inválida, 404 para um recurso inexistente.
- Valide todas as entradas do usuário no servidor, mesmo que o formulário já as valide no cliente.
Conclusão
O Flask ilustra perfeitamente a filosofia do Python: simples para começar, extensível para crescer. Agora você sabe criar rotas, gerar páginas com Jinja2, tratar formulários, expor uma API JSON, organizar seu código com blueprints e fazer o deploy com Gunicorn e nginx. O próximo passo natural: conectar um banco de dados com Flask-SQLAlchemy, gerenciar a autenticação com Flask-Login e proteger seus formulários com Flask-WTF.
Para se aprofundar, consulte a documentação oficial do Flask, notavelmente clara e completa.
Sobre InSkillCoach
Especialista em treinamento e tecnologias
Coach especializado em tecnologias avançadas e IA, apoiado pela GNeurone Inc.
Certificações:
- AWS Certified Solutions Architect – Professional
- Certificações Google Cloud
- Microsoft Certified: DevOps Engineer Expert
- Certified Kubernetes Administrator (CKA)
- CompTIA Security+
Comentários
Os comentários são alimentados pelo GitHub Discussions
Conecte-se com o GitHub para participar da discussão