Solucionado (ver solução)
Solucionado
(ver solução)
1
resposta

[Dúvida] Essa lógica de criar/reusar o banco pelo .env está correta?

Env:

DB_HOST=localhost
DB_USER=SEU_USUARIO_POSTGRES
DB_PASSWORD=SUA_SENHA

DB_ADMIN_DATABASE=postgres
DB_APPLICATION_DATABASE=escolasegundoDB

Config:

from dotenv import load_dotenv
import os

class Config:
    def __init__(self):
        load_dotenv()

        self.host = os.getenv("DB_HOST")
        self.user = os.getenv("DB_USER")
        self.password = os.getenv("DB_PASSWORD")

        self.admin_db = os.getenv("DB_ADMIN_DATABASE")
        self.app_db = os.getenv("DB_APPLICATION_DATABASE")

        if not self.app_db:
            raise RuntimeError("DB_APPLICATION_DATABASE não definido")

Criação manual do banco de dados:

import psycopg2
from Bd_ORMs.TreinarORm.config.config_pthon import Config

class DatabaseManual:
    def __init__(self):
        self.config = Config()
        self.conn = None

    def conectar_admin(self):
        self.conn = psycopg2.connect(
            host=self.config.host,
            user=self.config.user,
            password=self.config.password,
            database=self.config.admin_db
        )
        self.conn.autocommit = True
        print("Conectado ao banco ADMIN")

    def criar_banco(self):
        with self.conn.cursor() as cursor:
            cursor.execute(
                "SELECT 1 FROM pg_database WHERE datname = %s",
                (self.config.app_db,)
            )

            if not cursor.fetchone():
                sql = 'CREATE DATABASE "{}"'.format(self.config.app_db)
                cursor.execute(sql)
                print("Banco criado: {}".format(self.config.app_db))
            else:
                print("Banco já existe: {}".format(self.config.app_db))

    def conectar_aplicacao(self):
        self.conn.close()

        self.conn = psycopg2.connect(
            host=self.config.host,
            user=self.config.user,
            password=self.config.password,
            database=self.config.app_db
        )
        print("Conectado ao banco da aplicação")

    def testar_banco(self):
        with self.conn.cursor() as cursor:
            cursor.execute("SELECT current_database()")
            print(" Banco ativo: {}".format(cursor.fetchone()[0]))

    def fechar(self):
        if self.conn:
            self.conn.close()
            print(" Conexão fechada")

Conexão ORM com o banco de dados:

from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker, declarative_base
from Bd_ORMs.TreinarORm.config.config_pthon import Config


class DatabaseORM:
    def __init__(self):
        self.config = Config()

        self.engine = create_engine(
            "postgresql://{}:{}@{}/{}".format(
                self.config.user,
                self.config.password,
                self.config.host,
                self.config.app_db
            )
        )

        self.SessionLocal = sessionmaker(bind=self.engine)
        self.Base = declarative_base()

    def testar_conexao(self):
        with self.engine.connect() as conn:
            result = conn.execute(text("SELECT current_database()"))
            print(" ORM conectada no banco: {}".format(result.scalar()))

Main:

from Bd_ORMs.TreinarORm.database.db_manual import DatabaseManual
from Bd_ORMs.TreinarORm.database.db import DatabaseORM


if __name__ == "__main__":
    print("\n  ETAPA MANUAL")
    manual = DatabaseManual()
    manual.conectar_admin()
    manual.criar_banco()
    manual.conectar_aplicacao()
    manual.testar_banco()
    manual.fechar()

    print("\ ETAPA ORM")
    orm = DatabaseORM()
    orm.testar_conexao()

Professor(a), gostaria de validar se a arquitetura que estou usando para configuração e criação de banco de dados está correta, considerando boas práticas, segurança, reaproveitamento de código e aprendizado.

Minha aplicação funciona assim:

Uma classe Config centraliza a leitura do .env, que contém host, usuário, senha e nome do banco da aplicação.

Um módulo manual:
conecta no banco administrador;
verifica se o banco da aplicação existe;
cria o banco apenas se não existir;
testa a conexão.

Em seguida, a ORM (SQLAlchemy) conecta diretamente ao banco criado, usando o nome definido no .env.
Nas próximas execuções, o banco não é recriado, apenas reutilizado.

Caso eu crie outro sistema, posso reaproveitar o mesmo código, mudando apenas o nome do banco no .env.
Dessa forma, não é necessário criar o banco manualmente no pgAdmin nem escrever código de conexão separado para cada nova aplicação.


Minha dúvida é: essa lógica está correta? Criar o banco pelo código usando o .env e fazer a ORM se conectar ao banco já criado, de forma que, se o nome do banco no .env mudar, um novo banco seja criado automaticamente, e se o nome permanecer o mesmo, o banco existente seja reutilizado, sem precisar recriar manualmente ou escrever código de conexão separado pra cada app.

1 resposta
solução!

Oii, Samuel.

A sua lógica de automação pra criação do banco de dados tá correta e é muito funcional pra ambientes de desenvolvimento. Você criou um fluxo de "autoprovisionamento" que facilita muito o "onboarding" de novos projetos ou desenvolvedores, já que ninguém precisa abrir o pgAdmin para começar a trabalhar.

Alguns pontos importantes para você considerar na sua arquitetura:

O que tá excelente:

  • Centralização com .env: Usar variáveis de ambiente é a prática padrão de mercado (seguindo os princípios de The Twelve-Factor App). Isso garante que suas credenciais não fiquem expostas no código.
  • Idempotência: Sua lógica de verificar se o banco existe antes de tentar criar (if not cursor.fetchone()) torna o script "idempotente", ou seja, ele pode rodar várias vezes sem causar erros.
  • Separação de responsabilidades: Você dividiu bem a configuração (Config), a manipulação administrativa (DatabaseManual) e o uso da aplicação (DatabaseORM).

Pontos de atenção:

  1. Segurança em produção: Em ambientes de produção (nuvem), geralmente a aplicação não tem permissão de "Superuser" para criar bancos de dados. Por segurança, o banco é criado previamente pela infraestrutura e a aplicação recebe apenas uma credencial com acesso limitado àquele banco específico. Mantenha essa sua lógica como um facilitador de ambiente local/dev.
  2. SQL Injection:
    No seu método criar_banco, você usou .format(self.config.app_db). O PostgreSQL não permite passar nomes de identificadores (como nomes de bancos) via parâmetros tradicionais (%s), então o format é necessário. No entanto, para ser mais seguro, você poderia usar a biblioteca psycopg2.extensions.quote_ident ou a classe sql do psycopg2 para garantir que o nome do banco seja tratado corretamente e evitar falhas de segurança.
  3. Gerenciamento de conexões:
    No seu código manual, você abre e fecha a conexão. Lembre-se sempre de usar o bloco with (context manager) tanto para o cursor quanto para a connection (no caso do SQLAlchemy, o engine.connect() já suporta isso). Isso evita que conexões fiquem "penduradas" no banco de dados.

Sua abordagem demonstra um nível de maturidade muito bom, pois você tá pensando em automação e escalabilidade de código.

Conteúdos relacionados
Alura Conte com o apoio da comunidade Alura na sua jornada. Abraços e bons estudos!
a