// aula · Python + PostgreSQL · Conectando o mundo Python ao banco

Python e Banco de Dados
com PostgreSQL


Por que Python + PostgreSQL?

Uma dupla muito poderosa

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.

Conexão simples

Python tem bibliotecas maduras e bem documentadas para se conectar ao PostgreSQL — tanto de baixo nível (psycopg2) quanto de alto nível (SQLAlchemy).

Transações seguras

O PostgreSQL garante ACID — operações atômicas, consistentes, isoladas e duráveis — que o Python consegue aproveitar com simples blocos with.

Dados ricos

PostgreSQL suporta JSON, arrays, tipos geográficos e vetores. O Python consegue trabalhar com todos esses tipos de forma nativa.

ORM disponível

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 que é o 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

Terminal (bash)
# 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

Python — conexao.py
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()

Boa prática — use o gerenciador de contexto

Em vez de fechar manualmente, use with. Se der erro, o rollback acontece automaticamente. Se tudo der certo, o commit é feito.

Python — usando with
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

O que é CRUD?

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

Python — criar_tabela.py
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)

Python — inserir dados
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 faça interpolação de string no SQL!

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)

Python — buscar dados
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)

Python — atualizar dados
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)

Python — excluir dados
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étodoO que fazRetorna
execute(sql, params)Executa um comando SQLNone
fetchone()Retorna a próxima linha do resultadoTupla ou None
fetchall()Retorna todas as linhas do resultadoLista de tuplas
fetchmany(n)Retorna as próximas n linhasLista de tuplas
executemany(sql, lista)Executa o mesmo SQL para vários parâmetrosNone
rowcountNúmero de linhas afetadas pelo último comandoInteiro

ORM com SQLAlchemy

O que é ORM?

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

Terminal
pip install sqlalchemy psycopg2-binary

Definindo o modelo (a tabela como classe)

Python — models.py
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

Python — crud_orm.py
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ériopsycopg2SQLAlchemy ORM
Nível de abstraçãoBaixo — você escreve SQLAlto — você usa classes Python
ControleTotal — qualquer SQL funcionaBom — SQL bruto também disponível
Curva de aprendizadoBaixaMédia
ProdutividadeMédiaAlta para CRUD padrão
Ideal paraScripts, queries complexasAPIs, sistemas maiores
MigrationsManualAlembic (automático)

Boas práticas e segurança

Use variáveis de ambiente para credenciais

Nunca coloque senha no código! Use a biblioteca python-dotenv e um arquivo .env (que vai no .gitignore).

.env
DB_HOST=localhost
DB_NAME=meu_banco
DB_USER=postgres
DB_PASSWORD=minha_senha_secreta
DB_PORT=5432
Python — usando .env
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")
)

Parâmetros, não f-strings

Sempre use %s com tupla no psycopg2. Nunca formate SQL com f-string ou .format().

Sempre faça commit

INSERT, UPDATE e DELETE precisam de conn.commit() para persistir. Sem commit, as mudanças são descartadas.

Trate erros com try/except

Envolva operações em try/except e faça conn.rollback() em caso de erro para não deixar o banco num estado inconsistente.

Use connection pool

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

O que vamos construir

Uma agenda de contatos simples pelo terminal — você pode adicionar, listar, buscar e excluir contatos, com tudo salvo no PostgreSQL.

1
Estrutura do projeto
Organize os arquivos assim:
Estrutura
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
2
database.py — conexão reutilizável
database.py
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")
    )
3
agenda.py — funções CRUD completas
agenda.py
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.")
4
main.py — menu interativo
main.py
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