Django vs Flask: Qual Framework Escolher?

Comparação detalhada entre Django e Flask com exemplos de código, prós e contras, e recomendações para cada tipo de projeto em Python no Brasil.

6 min de leitura Equipe Python Brasil

Na hora de construir uma aplicação web com Python, dois nomes dominam a conversa: Django e Flask. Ambos são excelentes, mas servem para situações diferentes. Neste artigo, a gente vai comparar os dois frameworks de forma prática, com código e recomendações claras.

Visão Geral

Django: “Batteries Included”

Django é um framework full-stack que vem com tudo que você precisa para construir uma aplicação web robusta: ORM, sistema de templates, admin, autenticação, migrations e muito mais. A filosofia é “batteries included” — ou seja, tudo vem pronto para usar.

Flask: “Microframework”

Flask é um microframework que te dá o mínimo necessário para começar e deixa você escolher as ferramentas adicionais. A filosofia é dar liberdade ao desenvolvedor para escolher cada componente.

Comparação Rápida

AspectoDjangoFlask
TipoFull-stackMicroframework
Curva de aprendizadoMais íngremeMais suave
FlexibilidadeOpinativoLivre
ORMIncluso (Django ORM)Opcional (SQLAlchemy)
AdminInclusoNão tem (Flask-Admin)
AutenticaçãoInclusaExtensões
Template engineDjango TemplatesJinja2
Ideal paraProjetos médios/grandesAPIs, microsserviços

Começando com Flask

Flask é famoso pela simplicidade do “Hello World”:

# app.py
from flask import Flask, jsonify, request

app = Flask(__name__)

@app.route("/")
def index():
    return "Olá, Flask!"

@app.route("/api/saudacao/<nome>")
def saudacao(nome):
    return jsonify({
        "mensagem": f"Olá, {nome}!",
        "status": "sucesso"
    })

if __name__ == "__main__":
    app.run(debug=True)

API REST com Flask

from flask import Flask, jsonify, request, abort

app = Flask(__name__)

# "Banco de dados" em memória
tarefas = [
    {"id": 1, "titulo": "Estudar Python", "concluida": False},
    {"id": 2, "titulo": "Fazer exercícios", "concluida": True},
]

@app.route("/api/tarefas", methods=["GET"])
def listar_tarefas():
    return jsonify({"tarefas": tarefas})

@app.route("/api/tarefas/<int:tarefa_id>", methods=["GET"])
def obter_tarefa(tarefa_id):
    tarefa = next((t for t in tarefas if t["id"] == tarefa_id), None)
    if tarefa is None:
        abort(404)
    return jsonify(tarefa)

@app.route("/api/tarefas", methods=["POST"])
def criar_tarefa():
    if not request.json or "titulo" not in request.json:
        abort(400)

    nova_tarefa = {
        "id": tarefas[-1]["id"] + 1 if tarefas else 1,
        "titulo": request.json["titulo"],
        "concluida": False,
    }
    tarefas.append(nova_tarefa)
    return jsonify(nova_tarefa), 201

@app.route("/api/tarefas/<int:tarefa_id>", methods=["PUT"])
def atualizar_tarefa(tarefa_id):
    tarefa = next((t for t in tarefas if t["id"] == tarefa_id), None)
    if tarefa is None:
        abort(404)

    tarefa["titulo"] = request.json.get("titulo", tarefa["titulo"])
    tarefa["concluida"] = request.json.get("concluida", tarefa["concluida"])
    return jsonify(tarefa)

@app.route("/api/tarefas/<int:tarefa_id>", methods=["DELETE"])
def deletar_tarefa(tarefa_id):
    tarefa = next((t for t in tarefas if t["id"] == tarefa_id), None)
    if tarefa is None:
        abort(404)

    tarefas.remove(tarefa)
    return jsonify({"resultado": True})

if __name__ == "__main__":
    app.run(debug=True)

Estrutura de um projeto Flask

# Estrutura típica de projeto Flask
# meu_projeto/
# ├── app/
# │   ├── __init__.py        # Factory do app
# │   ├── models.py          # Modelos do banco
# │   ├── routes/
# │   │   ├── __init__.py
# │   │   ├── auth.py
# │   │   └── api.py
# │   ├── templates/
# │   │   └── base.html
# │   └── static/
# │       └── css/
# ├── config.py
# ├── requirements.txt
# └── run.py

# app/__init__.py - Application Factory Pattern
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

def create_app(config_name="development"):
    app = Flask(__name__)
    app.config.from_object(f"config.{config_name}")

    db.init_app(app)

    from app.routes.auth import auth_bp
    from app.routes.api import api_bp

    app.register_blueprint(auth_bp, url_prefix="/auth")
    app.register_blueprint(api_bp, url_prefix="/api")

    return app

Começando com Django

Django exige um pouco mais de setup inicial, mas entrega muita funcionalidade:

# Criando um projeto Django:
# django-admin startproject meu_projeto
# cd meu_projeto
# python manage.py startapp tarefas

# tarefas/models.py
from django.db import models

class Tarefa(models.Model):
    titulo = models.CharField(max_length=200)
    descricao = models.TextField(blank=True)
    concluida = models.BooleanField(default=False)
    criada_em = models.DateTimeField(auto_now_add=True)
    atualizada_em = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["-criada_em"]
        verbose_name_plural = "Tarefas"

    def __str__(self):
        return self.titulo

Admin automático com Django

# tarefas/admin.py
from django.contrib import admin
from .models import Tarefa

@admin.register(Tarefa)
class TarefaAdmin(admin.ModelAdmin):
    list_display = ["titulo", "concluida", "criada_em"]
    list_filter = ["concluida", "criada_em"]
    search_fields = ["titulo", "descricao"]
    list_editable = ["concluida"]

# Com essas poucas linhas, você tem um painel administrativo completo!
# Acesse em: http://localhost:8000/admin/

Views e URLs com Django

# tarefas/views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
from .models import Tarefa
import json

def lista_tarefas(request):
    tarefas = Tarefa.objects.all()
    return render(request, "tarefas/lista.html", {"tarefas": tarefas})

def detalhe_tarefa(request, pk):
    tarefa = get_object_or_404(Tarefa, pk=pk)
    return render(request, "tarefas/detalhe.html", {"tarefa": tarefa})

# API REST simples com Django puro
@require_http_methods(["GET", "POST"])
def api_tarefas(request):
    if request.method == "GET":
        tarefas = list(Tarefa.objects.values(
            "id", "titulo", "concluida", "criada_em"
        ))
        return JsonResponse({"tarefas": tarefas}, safe=False)

    elif request.method == "POST":
        dados = json.loads(request.body)
        tarefa = Tarefa.objects.create(
            titulo=dados["titulo"],
            descricao=dados.get("descricao", ""),
        )
        return JsonResponse({
            "id": tarefa.id,
            "titulo": tarefa.titulo,
            "concluida": tarefa.concluida,
        }, status=201)
# tarefas/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path("", views.lista_tarefas, name="lista_tarefas"),
    path("<int:pk>/", views.detalhe_tarefa, name="detalhe_tarefa"),
    path("api/", views.api_tarefas, name="api_tarefas"),
]

Django REST Framework

Para APIs REST profissionais, o Django REST Framework (DRF) é o padrão:

# pip install djangorestframework

# tarefas/serializers.py
from rest_framework import serializers
from .models import Tarefa

class TarefaSerializer(serializers.ModelSerializer):
    class Meta:
        model = Tarefa
        fields = ["id", "titulo", "descricao", "concluida", "criada_em"]
        read_only_fields = ["criada_em"]

# tarefas/views_api.py
from rest_framework import viewsets, filters
from rest_framework.decorators import action
from rest_framework.response import Response
from .models import Tarefa
from .serializers import TarefaSerializer

class TarefaViewSet(viewsets.ModelViewSet):
    queryset = Tarefa.objects.all()
    serializer_class = TarefaSerializer
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]
    search_fields = ["titulo", "descricao"]
    ordering_fields = ["criada_em", "titulo"]

    @action(detail=False, methods=["get"])
    def pendentes(self, request):
        pendentes = self.queryset.filter(concluida=False)
        serializer = self.get_serializer(pendentes, many=True)
        return Response(serializer.data)

    @action(detail=True, methods=["post"])
    def concluir(self, request, pk=None):
        tarefa = self.get_object()
        tarefa.concluida = True
        tarefa.save()
        return Response({"status": "tarefa concluída"})

Django ORM vs SQLAlchemy (Flask)

Django ORM

# Consultas com Django ORM
from tarefas.models import Tarefa
from django.db.models import Count, Q

# Criar
tarefa = Tarefa.objects.create(titulo="Estudar Django")

# Buscar
todas = Tarefa.objects.all()
pendentes = Tarefa.objects.filter(concluida=False)
tarefa = Tarefa.objects.get(id=1)

# Consultas complexas
resultado = Tarefa.objects.filter(
    Q(titulo__icontains="python") | Q(descricao__icontains="python"),
    concluida=False
).order_by("-criada_em")[:10]

# Agregações
stats = Tarefa.objects.aggregate(
    total=Count("id"),
    concluidas=Count("id", filter=Q(concluida=True)),
    pendentes=Count("id", filter=Q(concluida=False)),
)

SQLAlchemy com Flask

# Consultas com SQLAlchemy (Flask)
from app import db

class Tarefa(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    titulo = db.Column(db.String(200), nullable=False)
    concluida = db.Column(db.Boolean, default=False)

# Criar
tarefa = Tarefa(titulo="Estudar Flask")
db.session.add(tarefa)
db.session.commit()

# Buscar
todas = Tarefa.query.all()
pendentes = Tarefa.query.filter_by(concluida=False).all()
tarefa = Tarefa.query.get(1)

# Consultas complexas
from sqlalchemy import or_

resultado = Tarefa.query.filter(
    or_(
        Tarefa.titulo.ilike("%python%"),
        Tarefa.descricao.ilike("%python%")
    ),
    Tarefa.concluida == False
).order_by(Tarefa.criada_em.desc()).limit(10).all()

Quando Usar Cada Um?

Escolha Django quando:

  • Você está construindo uma aplicação web completa com frontend
  • Precisa de um painel admin pronto
  • O projeto é médio ou grande
  • Trabalha em equipe e quer convenções claras
  • Precisa de autenticação de usuários
  • Quer ORM, migrations e tudo integrado
  • Está construindo um e-commerce, CMS ou rede social

Escolha Flask quando:

  • Está construindo uma API simples ou microsserviço
  • Quer total controle sobre as ferramentas
  • O projeto é pequeno ou você está prototipando
  • Precisa de alta customização
  • Quer aprender como cada componente funciona internamente
  • Está construindo algo muito específico que não se encaixa nos padrões do Django

Ou considere FastAPI

Vale mencionar que, para APIs REST em 2026, o FastAPI tem ganhado muito espaço no Brasil e no mundo. Ele oferece performance superior, documentação automática e validação de dados nativa. A gente tem um artigo completo sobre FastAPI aqui no blog.

Mercado de Trabalho no Brasil

No Brasil, o Django ainda domina o mercado de vagas para desenvolvimento web com Python, especialmente em:

  • Startups e fintechs
  • Empresas de tecnologia
  • Órgãos governamentais
  • E-commerce

Flask aparece bastante em:

  • Microsserviços
  • APIs internas
  • Projetos de ciência de dados (dashboards)
  • Prototipagem rápida

A nossa recomendação: aprenda Django primeiro se quer desenvolver para web profissionalmente. Depois, aprenda Flask para entender como as coisas funcionam “por baixo dos panos”. E não deixe de dar uma olhada no FastAPI para APIs modernas.

No fim das contas, saber os dois (ou três!) frameworks te torna um profissional mais completo e versátil. O importante é começar por um e ir expandindo seus conhecimentos conforme a necessidade.

E

Equipe Python Brasil

Contribuidor do Python Brasil — Aprenda Python em Português