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!
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()erollback(). - 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.Rowtorna o código mais legível e menos propenso a erros. - Trate exceções: capture
sqlite3.IntegrityErroresqlite3.OperationalErrorem 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.
Equipe Python Brasil
Contribuidor do Python Brasil — Aprenda Python em Português