Python e SQLite — 2025 | Python Brasil

Aprenda a usar banco de dados SQLite com Python. CRUD completo, consultas e boas práticas com exemplos. Confira!

6 min de leitura Equipe Python Brasil

SQLite é um banco de dados leve, embutido e que não requer um servidor separado para funcionar. Ele armazena todo o banco em um único arquivo, tornando-o perfeito para aplicações desktop, protótipos, projetos pequenos e médios, e até mesmo para testes de aplicações que em produção usarão bancos maiores como PostgreSQL ou MySQL.

O Python traz o módulo sqlite3 na biblioteca padrão, o que significa que você pode começar a trabalhar com banco de dados sem instalar nada adicional. Neste artigo, vamos cobrir todas as operações fundamentais com exemplos práticos.

Conectando ao banco de dados

A conexão com SQLite é simples. Se o arquivo não existir, ele será criado automaticamente:

import sqlite3

# Conectando (cria o arquivo se não existir)
conexao = sqlite3.connect("minha_aplicacao.db")
cursor = conexao.cursor()

print("Conexão estabelecida com sucesso.")

# Sempre feche a conexão ao terminar
conexao.close()

A forma recomendada é usar o gerenciador de contexto, que garante o fechamento automático:

import sqlite3

with sqlite3.connect("minha_aplicacao.db") as conexao:
    cursor = conexao.cursor()
    # Operações com o banco aqui
    print("Operações concluídas.")
# A conexão é fechada automaticamente

Criando tabelas

Vamos criar um esquema para um sistema simples de gerenciamento de produtos:

import sqlite3

def criar_tabelas(conexao):
    """Cria as tabelas do sistema."""
    cursor = conexao.cursor()

    cursor.execute("""
        CREATE TABLE IF NOT EXISTS categorias (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            nome TEXT NOT NULL UNIQUE,
            descricao TEXT
        )
    """)

    cursor.execute("""
        CREATE TABLE IF NOT EXISTS produtos (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            nome TEXT NOT NULL,
            preco REAL NOT NULL,
            estoque INTEGER DEFAULT 0,
            categoria_id INTEGER,
            data_cadastro TEXT DEFAULT CURRENT_TIMESTAMP,
            FOREIGN KEY (categoria_id) REFERENCES categorias(id)
        )
    """)

    conexao.commit()
    print("Tabelas criadas com sucesso.")

with sqlite3.connect("loja.db") as conexao:
    criar_tabelas(conexao)

O CREATE TABLE IF NOT EXISTS evita erros caso as tabelas já existam. O commit() confirma as alterações no banco.

Inserindo dados (Create)

Para inserir dados, use placeholders (?) para evitar injeção de SQL:

import sqlite3

def inserir_categoria(conexao, nome, descricao):
    """Insere uma nova categoria."""
    cursor = conexao.cursor()
    try:
        cursor.execute(
            "INSERT INTO categorias (nome, descricao) VALUES (?, ?)",
            (nome, descricao)
        )
        conexao.commit()
        print(f"Categoria '{nome}' inserida com ID {cursor.lastrowid}")
        return cursor.lastrowid
    except sqlite3.IntegrityError:
        print(f"Categoria '{nome}' já existe.")
        return None

def inserir_produto(conexao, nome, preco, estoque, categoria_id):
    """Insere um novo produto."""
    cursor = conexao.cursor()
    cursor.execute(
        "INSERT INTO produtos (nome, preco, estoque, categoria_id) VALUES (?, ?, ?, ?)",
        (nome, preco, estoque, categoria_id)
    )
    conexao.commit()
    print(f"Produto '{nome}' inserido com ID {cursor.lastrowid}")
    return cursor.lastrowid

with sqlite3.connect("loja.db") as conexao:
    criar_tabelas(conexao)

    # Inserindo categorias
    cat_eletronicos = inserir_categoria(conexao, "Eletrônicos", "Produtos eletrônicos em geral")
    cat_acessorios = inserir_categoria(conexao, "Acessórios", "Acessórios para computador")

    # Inserindo produtos
    if cat_eletronicos:
        inserir_produto(conexao, "Notebook Dell", 4500.00, 10, cat_eletronicos)
        inserir_produto(conexao, "Monitor LG 27\"", 1599.00, 25, cat_eletronicos)

    if cat_acessorios:
        inserir_produto(conexao, "Mouse Logitech", 149.90, 100, cat_acessorios)
        inserir_produto(conexao, "Teclado Mecânico", 349.90, 50, cat_acessorios)

Nunca construa queries concatenando strings diretamente. Sempre use placeholders para prevenir ataques de injeção de SQL.

Inserção em lote

Para inserir muitos registros de uma vez, use executemany:

def inserir_produtos_lote(conexao, produtos):
    """Insere vários produtos de uma vez."""
    cursor = conexao.cursor()
    cursor.executemany(
        "INSERT INTO produtos (nome, preco, estoque, categoria_id) VALUES (?, ?, ?, ?)",
        produtos
    )
    conexao.commit()
    print(f"{cursor.rowcount} produtos inseridos.")

# Uso:
# lista_produtos = [
#     ("SSD 1TB", 399.90, 40, 1),
#     ("RAM 16GB", 289.90, 60, 1),
#     ("Webcam HD", 199.90, 35, 2),
# ]
# inserir_produtos_lote(conexao, lista_produtos)

Consultando dados (Read)

As consultas retornam tuplas por padrão, mas podemos configurar para retornar dicionários:

import sqlite3

def consultar_todos_produtos(conexao):
    """Retorna todos os produtos."""
    conexao.row_factory = sqlite3.Row
    cursor = conexao.cursor()
    cursor.execute("""
        SELECT p.id, p.nome, p.preco, p.estoque, c.nome as categoria
        FROM produtos p
        LEFT JOIN categorias c ON p.categoria_id = c.id
        ORDER BY p.nome
    """)
    return cursor.fetchall()

def buscar_produto_por_nome(conexao, termo):
    """Busca produtos pelo nome."""
    cursor = conexao.cursor()
    cursor.execute(
        "SELECT id, nome, preco, estoque FROM produtos WHERE nome LIKE ?",
        (f"%{termo}%",)
    )
    return cursor.fetchall()

def produtos_com_estoque_baixo(conexao, limite=10):
    """Retorna produtos com estoque abaixo do limite."""
    cursor = conexao.cursor()
    cursor.execute(
        "SELECT nome, estoque FROM produtos WHERE estoque < ? ORDER BY estoque",
        (limite,)
    )
    return cursor.fetchall()

with sqlite3.connect("loja.db") as conexao:
    criar_tabelas(conexao)

    print("Todos os produtos:")
    for produto in consultar_todos_produtos(conexao):
        print(f"  {produto['nome']} - R$ {produto['preco']:.2f} ({produto['estoque']} un.)")

O sqlite3.Row permite acessar colunas tanto por índice quanto por nome, tornando o código mais legível.

Atualizando dados (Update)

def atualizar_preco(conexao, produto_id, novo_preco):
    """Atualiza o preço de um produto."""
    cursor = conexao.cursor()
    cursor.execute(
        "UPDATE produtos SET preco = ? WHERE id = ?",
        (novo_preco, produto_id)
    )
    conexao.commit()
    if cursor.rowcount > 0:
        print(f"Preço do produto {produto_id} atualizado para R$ {novo_preco:.2f}")
    else:
        print(f"Produto {produto_id} não encontrado.")

def atualizar_estoque(conexao, produto_id, quantidade):
    """Adiciona ou remove quantidade do estoque."""
    cursor = conexao.cursor()
    cursor.execute(
        "UPDATE produtos SET estoque = estoque + ? WHERE id = ?",
        (quantidade, produto_id)
    )
    conexao.commit()
    print(f"Estoque do produto {produto_id} ajustado em {quantidade} unidades.")

Removendo dados (Delete)

def remover_produto(conexao, produto_id):
    """Remove um produto pelo ID."""
    cursor = conexao.cursor()
    cursor.execute("DELETE FROM produtos WHERE id = ?", (produto_id,))
    conexao.commit()
    if cursor.rowcount > 0:
        print(f"Produto {produto_id} removido.")
    else:
        print(f"Produto {produto_id} não encontrado.")

Classe gerenciadora do banco

Para projetos maiores, encapsule as operações em uma classe:

import sqlite3
from contextlib import contextmanager

class GerenciadorLoja:
    def __init__(self, caminho_db):
        self.caminho_db = caminho_db

    @contextmanager
    def conectar(self):
        conexao = sqlite3.connect(self.caminho_db)
        conexao.row_factory = sqlite3.Row
        try:
            yield conexao
            conexao.commit()
        except Exception as e:
            conexao.rollback()
            raise e
        finally:
            conexao.close()

    def listar_produtos(self):
        with self.conectar() as conn:
            cursor = conn.cursor()
            cursor.execute("SELECT * FROM produtos ORDER BY nome")
            return [dict(row) for row in cursor.fetchall()]

    def relatorio_estoque(self):
        with self.conectar() as conn:
            cursor = conn.cursor()
            cursor.execute("""
                SELECT c.nome as categoria,
                       COUNT(p.id) as total_produtos,
                       SUM(p.estoque) as total_estoque,
                       SUM(p.preco * p.estoque) as valor_total
                FROM produtos p
                JOIN categorias c ON p.categoria_id = c.id
                GROUP BY c.nome
            """)
            return [dict(row) for row in cursor.fetchall()]

# Uso:
# loja = GerenciadorLoja("loja.db")
# for produto in loja.listar_produtos():
#     print(produto)

Boas práticas com SQLite em Python

  • Use placeholders: nunca concatene valores diretamente na query SQL.
  • Use transações: agrupe operações relacionadas em transações com commit() e rollback().
  • Feche conexões: use gerenciadores de contexto (with) para garantir o fechamento.
  • Indexe colunas frequentes: crie índices em colunas usadas em WHERE e JOIN para melhorar a performance.
  • Use row_factory: sqlite3.Row torna o código mais legível e menos propenso a erros.
  • Trate exceções: capture sqlite3.IntegrityError e sqlite3.OperationalError em operações críticas.

Conclusão

SQLite com Python é uma combinação poderosa para armazenamento de dados local. O módulo sqlite3 fornece tudo o que você precisa para criar aplicações com persistência de dados de forma simples e eficiente. Desde protótipos rápidos até aplicações de tamanho moderado, o SQLite atende com excelência.

Como próximos passos, explore ORMs como SQLAlchemy para abstrair as queries SQL, aprenda sobre migrações de banco de dados e, quando suas aplicações crescerem, considere a transição para PostgreSQL ou MySQL, mantendo a mesma lógica de código.

E

Equipe Python Brasil

Contribuidor do Python Brasil — Aprenda Python em Português