// aula · Python + PostgreSQL · Conectando o mundo Python ao banco
Por que Python + PostgreSQL?
Python é a linguagem mais popular do mundo para desenvolvimento back-end, ciência de dados e automação. PostgreSQL é o banco de dados relacional open source mais robusto. Juntos, eles formam a base de sistemas usados por empresas como Instagram, Nubank e OpenAI.
Python tem bibliotecas maduras e bem documentadas para se conectar ao PostgreSQL — tanto de baixo nível (psycopg2) quanto de alto nível (SQLAlchemy).
O PostgreSQL garante ACID — operações atômicas, consistentes, isoladas e duráveis — que o Python consegue aproveitar com simples blocos with.
PostgreSQL suporta JSON, arrays, tipos geográficos e vetores. O Python consegue trabalhar com todos esses tipos de forma nativa.
Com SQLAlchemy, você pode trabalhar com o banco usando classes Python, sem escrever SQL diretamente — mas com total flexibilidade quando precisar.
Conectando com psycopg2
O psycopg2 é o adaptador PostgreSQL mais popular para Python. Ele faz a "ponte" entre o código Python e o banco, traduzindo chamadas Python em protocolo PostgreSQL. É baixo nível — você escreve SQL diretamente, com total controle.
Instalação
# Instale via pip pip install psycopg2-binary # Se quiser a versão compilada (mais performática em produção) pip install psycopg2
Primeira conexão
import psycopg2 # Conectando ao banco de dados conn = psycopg2.connect( host="localhost", port=5432, database="meu_banco", user="postgres", password="minha_senha" ) # Criando um cursor (o objeto que executa comandos SQL) cur = conn.cursor() # Testando a conexão cur.execute("SELECT version();") versao = cur.fetchone() print(f"Conectado ao PostgreSQL: {versao[0]}") # Sempre feche o cursor e a conexão! cur.close() conn.close()
Em vez de fechar manualmente, use with. Se der erro, o rollback acontece automaticamente. Se tudo der certo, o commit é feito.
import psycopg2 with psycopg2.connect( host="localhost", database="meu_banco", user="postgres", password="minha_senha" ) as conn: with conn.cursor() as cur: cur.execute("SELECT version();") print(cur.fetchone()) # Conexão fechada automaticamente aqui!
CRUD com Python puro
CRUD é o acrônimo das 4 operações básicas de qualquer sistema que trabalha com dados: Create (criar), Read (ler), Update (atualizar) e Delete (excluir). No SQL, essas operações são INSERT, SELECT, UPDATE e DELETE.
Criando a tabela
import psycopg2 def criar_tabela(conn): with conn.cursor() as cur: cur.execute(""" CREATE TABLE IF NOT EXISTS usuarios ( id SERIAL PRIMARY KEY, nome VARCHAR(100) NOT NULL, email VARCHAR(150) UNIQUE NOT NULL, idade INTEGER, criado_em TIMESTAMP DEFAULT NOW() ); """) conn.commit() print("Tabela criada com sucesso!")
C — Create (INSERT)
def inserir_usuario(conn, nome, email, idade): with conn.cursor() as cur: # SEMPRE use %s para evitar SQL Injection! cur.execute( "INSERT INTO usuarios (nome, email, idade) VALUES (%s, %s, %s) RETURNING id;", (nome, email, idade) ) novo_id = cur.fetchone()[0] conn.commit() print(f"Usuário inserido com ID: {novo_id}") return novo_id # Usando a função: inserir_usuario(conn, "Lucilia Rosa", "lucilia@email.com", 25)
Nunca use f"...WHERE nome = '{nome}'" — isso abre brecha para SQL Injection. Sempre passe os valores como parâmetros com %s e uma tupla.
R — Read (SELECT)
def listar_usuarios(conn): with conn.cursor() as cur: cur.execute("SELECT id, nome, email, idade FROM usuarios ORDER BY nome;") usuarios = cur.fetchall() # retorna lista de tuplas for u in usuarios: print(f"[{u[0]}] {u[1]} — {u[2]} ({u[3]} anos)") return usuarios def buscar_por_email(conn, email): with conn.cursor() as cur: cur.execute( "SELECT * FROM usuarios WHERE email = %s;", (email,) # note a vírgula — precisa ser tupla! ) return cur.fetchone()
U — Update (UPDATE)
def atualizar_email(conn, user_id, novo_email): with conn.cursor() as cur: cur.execute( "UPDATE usuarios SET email = %s WHERE id = %s;", (novo_email, user_id) ) conn.commit() print(f"Email do usuário {user_id} atualizado!")
D — Delete (DELETE)
def excluir_usuario(conn, user_id): with conn.cursor() as cur: cur.execute( "DELETE FROM usuarios WHERE id = %s;", (user_id,) ) conn.commit() print(f"Usuário {user_id} excluído.")
Métodos importantes do cursor
| Método | O que faz | Retorna |
|---|---|---|
execute(sql, params) | Executa um comando SQL | None |
fetchone() | Retorna a próxima linha do resultado | Tupla ou None |
fetchall() | Retorna todas as linhas do resultado | Lista de tuplas |
fetchmany(n) | Retorna as próximas n linhas | Lista de tuplas |
executemany(sql, lista) | Executa o mesmo SQL para vários parâmetros | None |
rowcount | Número de linhas afetadas pelo último comando | Inteiro |
ORM com SQLAlchemy
ORM (Object-Relational Mapping) é uma técnica que mapeia tabelas do banco de dados para classes Python. Com SQLAlchemy, cada tabela vira uma classe e cada linha vira um objeto — você trabalha com Python, e ele gera o SQL por baixo.
Instalação
pip install sqlalchemy psycopg2-binary
Definindo o modelo (a tabela como classe)
from sqlalchemy import create_engine, Column, Integer, String, DateTime from sqlalchemy.orm import declarative_base, Session from datetime import datetime # URL de conexão: dialect+driver://user:password@host/dbname DATABASE_URL = "postgresql+psycopg2://postgres:minha_senha@localhost/meu_banco" engine = create_engine(DATABASE_URL, echo=True) Base = declarative_base() # Cada classe = uma tabela no banco class Usuario(Base): __tablename__ = "usuarios" id = Column(Integer, primary_key=True) nome = Column(String(100), nullable=False) email = Column(String(150), unique=True, nullable=False) idade = Column(Integer) criado_em = Column(DateTime, default=datetime.now) def __repr__(self): return f"<Usuario {self.nome} ({self.email})>" # Cria as tabelas no banco se não existirem Base.metadata.create_all(engine)
CRUD com SQLAlchemy
from sqlalchemy.orm import Session from models import engine, Usuario # ── CREATE ───────────────────────────────────────── with Session(engine) as session: novo = Usuario(nome="Lucilia Rosa", email="lucilia@email.com", idade=25) session.add(novo) session.commit() print(f"Criado: {novo}") # ── READ ─────────────────────────────────────────── with Session(engine) as session: todos = session.query(Usuario).all() for u in todos: print(u) # Busca por filtro lucilia = session.query(Usuario).filter_by(nome="Lucilia Rosa").first() # ── UPDATE ───────────────────────────────────────── with Session(engine) as session: u = session.get(Usuario, 1) # busca pelo ID if u: u.email = "novo@email.com" session.commit() # ── DELETE ───────────────────────────────────────── with Session(engine) as session: u = session.get(Usuario, 1) if u: session.delete(u) session.commit()
psycopg2 vs SQLAlchemy — quando usar cada um?
| Critério | psycopg2 | SQLAlchemy ORM |
|---|---|---|
| Nível de abstração | Baixo — você escreve SQL | Alto — você usa classes Python |
| Controle | Total — qualquer SQL funciona | Bom — SQL bruto também disponível |
| Curva de aprendizado | Baixa | Média |
| Produtividade | Média | Alta para CRUD padrão |
| Ideal para | Scripts, queries complexas | APIs, sistemas maiores |
| Migrations | Manual | Alembic (automático) |
Boas práticas e segurança
Nunca coloque senha no código! Use a biblioteca python-dotenv e um arquivo .env (que vai no .gitignore).
DB_HOST=localhost DB_NAME=meu_banco DB_USER=postgres DB_PASSWORD=minha_senha_secreta DB_PORT=5432
import os from dotenv import load_dotenv import psycopg2 load_dotenv() # carrega o arquivo .env conn = psycopg2.connect( host=os.getenv("DB_HOST"), database=os.getenv("DB_NAME"), user=os.getenv("DB_USER"), password=os.getenv("DB_PASSWORD"), port=os.getenv("DB_PORT", "5432") )
Sempre use %s com tupla no psycopg2. Nunca formate SQL com f-string ou .format().
INSERT, UPDATE e DELETE precisam de conn.commit() para persistir. Sem commit, as mudanças são descartadas.
Envolva operações em try/except e faça conn.rollback() em caso de erro para não deixar o banco num estado inconsistente.
Em aplicações web, use pool de conexões (psycopg2.pool ou o pool do SQLAlchemy) para não abrir e fechar conexão a cada requisição.
Mini projeto — Agenda de Contatos
Uma agenda de contatos simples pelo terminal — você pode adicionar, listar, buscar e excluir contatos, com tudo salvo no PostgreSQL.
agenda/ ├── .env # credenciais (não sobe pro git!) ├── .gitignore # inclui .env ├── database.py # conexão com o banco ├── models.py # criação da tabela ├── agenda.py # funções CRUD └── main.py # menu interativo
import os import psycopg2 from dotenv import load_dotenv load_dotenv() def get_connection(): return psycopg2.connect( host=os.getenv("DB_HOST", "localhost"), database=os.getenv("DB_NAME", "agenda_db"), user=os.getenv("DB_USER", "postgres"), password=os.getenv("DB_PASSWORD"), port=os.getenv("DB_PORT", "5432") )
from database import get_connection def inicializar(): with get_connection() as conn, conn.cursor() as cur: cur.execute(""" CREATE TABLE IF NOT EXISTS contatos ( id SERIAL PRIMARY KEY, nome VARCHAR(100) NOT NULL, telefone VARCHAR(20), email VARCHAR(150) ); """) conn.commit() def adicionar(nome, telefone, email): with get_connection() as conn, conn.cursor() as cur: cur.execute( "INSERT INTO contatos (nome, telefone, email) VALUES (%s, %s, %s);", (nome, telefone, email) ) conn.commit() print(f"✅ {nome} adicionado(a) à agenda!") def listar(): with get_connection() as conn, conn.cursor() as cur: cur.execute("SELECT id, nome, telefone, email FROM contatos ORDER BY nome;") contatos = cur.fetchall() if not contatos: print("Agenda vazia.") return print("\n── Sua Agenda ──────────────────") for c in contatos: print(f"[{c[0]}] {c[1]} | 📞 {c[2]} | ✉ {c[3]}") print("────────────────────────────────\n") def buscar(nome): with get_connection() as conn, conn.cursor() as cur: cur.execute( "SELECT * FROM contatos WHERE nome ILIKE %s;", (f"%{nome}%",) ) return cur.fetchall() def excluir(contato_id): with get_connection() as conn, conn.cursor() as cur: cur.execute("DELETE FROM contatos WHERE id = %s;", (contato_id,)) conn.commit() print(f"🗑️ Contato {contato_id} removido.")
from agenda import inicializar, adicionar, listar, buscar, excluir inicializar() while True: print("\n📓 Agenda de Contatos") print("1 — Adicionar contato") print("2 — Listar contatos") print("3 — Buscar por nome") print("4 — Excluir contato") print("0 — Sair") opcao = input("\nEscolha: ") if opcao == "1": nome = input("Nome: ") telefone = input("Telefone: ") email = input("E-mail: ") adicionar(nome, telefone, email) elif opcao == "2": listar() elif opcao == "3": termo = input("Buscar por nome: ") resultados = buscar(termo) for c in resultados: print(c) elif opcao == "4": cid = int(input("ID do contato a excluir: ")) excluir(cid) elif opcao == "0": print("Até mais! 👋") break