JSON foi um SUPER passo, mas começa a complicar quando os dados crescem. Vem aí o TinyDB — um banco de verdade dentro de um arquivinho. Hoje você vai fazer CRUD completo em Python, sem SQL, sem servidor, sem stress.
No Capítulo 08 você aprendeu a salvar dados em .json com
json.dump() e json.load(). Foi um marco — a partir dali seus dados
sobrevivem ao programa fechar.
Pensa só: hoje, pra atualizar UM aluno no JSON, você precisa
ler o arquivo inteiro, modificar a lista e
regravar tudo. Pra buscar alguém, é um for na mão.
Funciona? Funciona. Mas dá pra fazer melhor.
E se a gente pudesse falar com o arquivo assim: "me dá quem se chama Ana" ou "atualiza só a idade do João, deixa o resto em paz"? Esse é o trabalho de um banco de dados.
Com JSON, é como se você tivesse uma caixa fechada com elástico. Pra mexer numa ficha, abre a caixa, tira tudo, mexe, guarda tudo de novo.
Com o TinyDB é como ter uma caixa com divisórias e etiquetas. Você fala: "puxa a ficha da Ana" e ela vem. "Troca a idade dela", e só ela muda. O resto fica intocado. 📂
Lembra do sistema_cadastro_json.py do capítulo passado? Ele fazia mais ou menos isso
toda vez que você atualizava ou removia alguém:
"É aí que entra um banco de dados de verdade. 🪄"
O TinyDB é uma biblioteca Python (a gente instala com
pip) que oferece um banco de dados completo guardado num único
arquivo .json. Sem SQL, sem servidor, sem senha, sem porta.
O melhor: ele trabalha com o que você já sabe —
dicionários (cap 07) e listas (cap 04).
A API é praticamente "escreve em português":
db.insert(...), db.all(), db.search(...).
A lista de dicionários do cap 07 era um caderno que rasgava ao fechar o programa.
O JSON do cap 08 é um caderno guardado na gaveta — mas pra mexer numa ficha, tem que tirar tudo da gaveta, mexer e guardar tudo de novo.
O TinyDB é a gaveta com fichas etiquetadas. Você fala "fichas da turma de Python", ele te entrega só essas. Você pede "atualiza idade da Ana", ele atualiza só ela. 📂
O TinyDB não vem com o Python — é uma biblioteca da comunidade. Pra instalar, abra o Terminal do PyCharm e rode:
pip é o "loja de apps" do Python. Você pede uma biblioteca, ele baixa e
deixa pronta pra usar com import. Roda só uma vez por projeto —
depois o TinyDB já fica disponível.
from tinydb import TinyDB. Se rodar sem erro, tá tudo certo.
Se aparecer ModuleNotFoundError, o pip não encontrou — instala de novo!
Create, Read, Update, Delete. Bora ver cada uma — uma linha por vez. Os 4 primeiros a gente faz junto.
db.insert()
Crie o arquivo primeiro_tinydb.py, escreva esse código e rode:
from tinydb import TinyDB db = TinyDB("alunos.json") db.insert({"nome": "Ana", "idade": 22, "curso": "Python"}) db.insert({"nome": "João", "idade": 25, "curso": "Java"}) print("✅ Dados salvos com sucesso!")
open(), sem json.dump(), sem with.
Cada db.insert() recebe um dicionário — exatamente o que você já sabe usar.
O TinyDB salva no disco automaticamente.
O TinyDB criou automaticamente o arquivo alunos.json na mesma pasta do script.
Se você abrir, vai ver isso aqui:
{
"_default": {
"1": {"nome": "Ana", "idade": 22, "curso": "Python"},
"2": {"nome": "João", "idade": 25, "curso": "Java"}
}
}
"_default" é o nome
da tabela padrão — a gente fala de tabelas mais pra frente!
db.all()
O db.all() traz todos os registros do banco —
já no formato que você conhece: uma lista em que cada item é um
dicionário. Dá pra percorrer com for, contar com len(),
tudo do que já vimos!
from tinydb import TinyDB db = TinyDB("alunos.json") dados = db.all() print("📂 Dados carregados:") for aluno in dados: print(aluno)
Rode primeiro_tinydb.py uma vez. Depois feche o terminal,
abra de novo, rode ler_tinydb.py — os dados continuam lá! 🎉
É essa a mágica de um banco de verdade.
db.search() + Query
Pra buscar, importa também o Query — ele é o "interrogador" do banco.
from tinydb import TinyDB, Query db = TinyDB("alunos.json") q = Query() resultado = db.search(q.nome == "Ana") print(resultado) # [{'nome': 'Ana', 'idade': 22, 'curso': 'Python'}]
q.nome == "Ana" quer dizer:
"o campo nome é igual a 'Ana'".
E db.search(...) traz todos os registros que batem com essa condição,
numa lista. Se ninguém bater, vem uma lista vazia [].
(q.curso == "Python") & (q.idade >= 18)
(use & e |, não and/or, é uma pegadinha do TinyDB!).
Vamos brincar disso no desafio.
from tinydb import where e
db.search(where("nome") == "Ana"). Faz o mesmo. Mas Query()
é o jeito moderno e flexível — vamos seguir com ele.
db.update()from tinydb import TinyDB, Query db = TinyDB("alunos.json") q = Query() db.update({"idade": 23}, q.nome == "Ana") print("✅ Idade atualizada!")
"No registro onde o nome é Ana, muda a idade pra 23."
Repara na ordem: primeiro o que mudar (dicionário com os campos novos), depois onde mudar (a query). Os outros campos da Ana ficam intocados, e os outros alunos não são afetados.
ler_tinydb.py. A Ana agora aparece com
idade 23, e o João continua com 25. Sem você ter mexido em mais nada!
db.remove()from tinydb import TinyDB, Query db = TinyDB("alunos.json") q = Query() db.remove(q.nome == "João") print("✅ João removido do banco.")
remove() apaga permanentemente do disco.
Diferente de uma lista que volta sozinha quando você reinicia o programa,
aqui o dado foi mesmo. Se chamar db.remove(q.curso == "Python"),
some todo mundo que faz Python.
db.search(...) com a mesma query
pra ver o que vai ser apagado. Confere, e só então remove.
Em sistemas reais, sempre tem uma confirmação antes do delete!
É a sigla mágica de todo banco de dados do mundo — do TinyDB ao MySQL, do MongoDB ao PostgreSQL. Quatro operações básicas. No TinyDB cada uma é uma linha:
| Operação | Código TinyDB | O que faz |
|---|---|---|
| CREATE | db.insert({"nome": "Ana"}) |
Adiciona um novo registro |
| READ | db.all() · db.search(q.nome == "Ana") |
Lista tudo ou filtra |
| UPDATE | db.update({"idade": 23}, q.nome == "Ana") |
Altera campos de quem bate na query |
| DELETE | db.remove(q.nome == "João") |
Apaga quem bate na query |
"Quatro operações. Quatro linhas. Sem SQL. Tudo Python." 🐍
db.update()?db.table(...)
Até agora, todos os insert() caíram na "tabela padrão" (lembra do
"_default" no JSON?). Mas e se a gente quiser separar
alunos e professores no mesmo arquivo, sem misturar?
from tinydb import TinyDB db = TinyDB("escola.json") alunos = db.table("alunos") professores = db.table("professores") alunos.insert({"nome": "Ana", "idade": 22}) professores.insert({"nome": "Kelson", "disciplina": "Python"})
O arquivo escola.json é o armário. Cada
db.table("...") é uma gaveta. A gaveta de alunos não se mistura
com a de professores. Os dois moram no mesmo arquivo, mas em "andares" diferentes. 🗄️
{
"alunos": {
"1": {"nome": "Ana", "idade": 22}
},
"professores": {
"1": {"nome": "Kelson", "disciplina": "Python"}
}
}
Cada tabela é uma "seção" no JSON. Tudo organizado, sem mistura.
alunos.search(...), professores.update(...) — todos os métodos que
a gente já viu funcionam direitinho em cada tabela individualmente.
Aqui é como se o seu script Python tivesse rodado e você estivesse olhando o banco.
Repare como o estado da tabela muda a cada operação, igualzinho aconteceria no
alunos.json de verdade:
Depois de inserir 2, atualizar a Ana e remover o João:
| id | nome | idade | curso |
|---|---|---|---|
| 1 | Ana | 23 ✏️ | Python |
| 2 | João | 25 | Java |
Não se assusta se você cair em um desses — é praticamente um rito de passagem. Vamos olhar os três bugs mais comuns, e como o BugZilla foge:
Olha esse código:
from tinydb import TinyDB, Query db = TinyDB("alunos.json") # ❌ ERRADO: faltou o () db.search(Query.nome == "Ana") # AttributeError: type object 'Query' has no attribute 'nome'
Query é uma classe. Pra usar você precisa
instanciar — chamar com parênteses, criando um "objeto de busca".
q = Query() e depois db.search(q.nome == "Ana").
O parêntese é a parte importante — é ele que cria seu interrogador.
update# ❌ ERRADO: query primeiro, dicionário depois db.update(q.nome == "Ana", {"idade": 23}) # ✅ CERTO: dicionário primeiro, query depois db.update({"idade": 23}, q.nome == "Ana")
and/or em vez de &/|# ❌ ERRADO: usar and/or em queries db.search(q.curso == "Python" and q.idade >= 18) # ✅ CERTO: use & e | (com parênteses!) db.search((q.curso == "Python") & (q.idade >= 18)) db.search((q.curso == "Python") | (q.curso == "Java"))
and e or são do Python "puro" e não
conseguem combinar duas queries. Use & (E) e | (OU) — e
SEMPRE com parênteses em volta de cada condição. É chato, mas é assim.
AttributeError (esqueceu o () do Query)
ou TypeError (combinou queries com and). Já sabe onde olhar!
Lembra do sistema_cadastro_json.py do Capítulo 08? Aquele com menu
(1- adicionar, 2- listar, 3- buscar, 0- sair)? Reescreva ele usando TinyDB.
Crie sistema_cadastro_tinydb.py com:
1️⃣ from tinydb import TinyDB, Query e db = TinyDB("alunos.json")
2️⃣ Uma função adicionar_aluno() que pede nome, idade (com int())
e curso, e usa db.insert(...).
3️⃣ Uma função listar_alunos() que percorre db.all() e imprime tudo
bonitinho.
4️⃣ Uma função buscar_por_nome() que pede o nome e usa
db.search(q.nome == nome).
5️⃣ Um menu em while True: com if/elif chamando cada função.
Saída quando op == "0".
🧪 O grande teste: quando terminar, rode o programa, cadastre 2 alunos, saia, feche o terminal, abra de novo e rode tudo de novo — os alunos continuam lá! É a primeira vez que seu programa tem memória de verdade. 🧠
Você já fez praticamente esse código no cap 07 e cap 08. A diferença agora é só:
a lista virou db, e o append virou insert.
Você consegue!
from tinydb import TinyDB, Query db = TinyDB("alunos.json") q = Query() def mostrar_menu(): print("\n=== Sistema de Alunos ===") print("1 - Adicionar aluno") print("2 - Listar alunos") print("3 - Buscar por nome") print("0 - Sair") def adicionar_aluno(): nome = input("Nome: ") idade = int(input("Idade: ")) curso = input("Curso: ") db.insert({"nome": nome, "idade": idade, "curso": curso}) print("✅ Aluno cadastrado!") def listar_alunos(): alunos = db.all() if len(alunos) == 0: print("Nenhum aluno cadastrado.") return for aluno in alunos: print(f"- {aluno['nome']}, {aluno['idade']} anos, {aluno['curso']}") print(f"Total: {len(alunos)} aluno(s)") def buscar_por_nome(): nome = input("Nome para buscar: ") resultado = db.search(q.nome == nome) if len(resultado) == 0: print("Nenhum aluno encontrado.") else: for aluno in resultado: print(f"- {aluno['nome']}, {aluno['idade']} anos, {aluno['curso']}") while True: mostrar_menu() op = input("Escolha: ") if op == "1": adicionar_aluno() elif op == "2": listar_alunos() elif op == "3": buscar_por_nome() elif op == "0": print(f"Total no banco: {len(db.all())} 👋") break
sistema_cadastro_json.py do cap 08,
esse código é menor. Sumiram o json.dump, o json.load,
o open() com with… O TinyDB cuida de tudo.
db.update) e uma 5 - remover aluno
(com db.remove). Agora você tem CRUD COMPLETO!
.json, sem SQL, sem servidor.Query(), inverter ordem do update, usar and/or em vez de &/|.
"Agora seus programas têm memória DE VERDADE.
Os dados sobrevivem mesmo depois de fechar o terminal." 🧠💾