Ir para o conteúdo

Changelog

📝 Changelog – Novos Módulos e Funcionalidades


📚 Registry.pyRegistro Global de Modelos e Cache

🗂️ Estrutura Global

  • _model_registry: armazena todas as classes Model já geradas e disponíveis para autocomplete e reuso.
  • _model_cache: mapeia modelos por (table_name, conn_id) para evitar recriação redundante.
  • _query_result_cache: cache em memória de resultados de queries, com TTL, usado em QuerySet.

Este módulo fornece a base para funcionalidades como: - Cache de queries (com live() para bypass). - Reload automático de modelos. - Geração de stubs baseados em modelos carregados dinamicamente.


📦 Model_cache.pyPersistência de Modelos em Disco

🔐 Criptografia e Armazenamento

  • Implementado mecanismo de cache criptografado de modelos em .wbmodels/ com chave Fernet salva em .wbormkey.
  • Os modelos são serializados com pickle, criptografados e salvos com a extensão .wbm.

📤 Funções Principais

  • save_model_to_disk(table_name, model_cls)
  • Salva um modelo ORM como dicionário de atributos serializáveis (campos e relações).
  • try_load_model_from_disk(table_name, conn)
  • Tenta carregar um modelo previamente salvo, reconstruindo a classe com ModelMeta, registrando no cache e módulo principal.
  • generate_model_stub(output_path)
  • Gera automaticamente o arquivo .pyi para autocomplete com base nos modelos registrados.
  • Produz anotações de tipo para editores com base em campos e nullability.

🔑 Segurança

  • Utiliza cryptography.fernet.Fernet para garantir que o cache seja seguro, mesmo se exposto.

🧠 Expressions.pyExpressões SQL Customizadas

📐 Expressões Declarativas

  • Classe Expression permite construir expressões SQL de forma fluida e segura:
  • Operadores implementados: ==, !=, >, <, >=, <=.
  • Integração com valores do tipo datetime com conversão automática.

🔧 Funções utilitárias para consultas

  • col(name) – cria expressão com base em nome de coluna.
  • date(field) – envolve campo com DATE(...) do Informix.
  • now() – insere a expressão CURRENT.
  • raw(expr) – insere SQL arbitrário diretamente.
  • format_informix_datetime(value) – converte datetime ou str para formato DATETIME(...) YEAR TO SECOND, suportando ISO8601 ou fallback para strings brutas.

Essas funções já estão expostas no pacote principal via __init__.py, permitindo:

from wborm import col, now, raw


📝 Utils.py

🚀 Novas Funcionalidades

  • Persistência de modelos em disco (cache local)
  • Adição da verificação de modelos armazenados via try_load_model_from_disk() antes de gerar novamente.
  • Os modelos gerados são salvos com save_model_to_disk() para reutilização futura sem nova introspecção.

  • Injeção automática no escopo global

  • Nova funcionalidade com inject_globals=True para permitir que os modelos sejam automaticamente atribuídos a variáveis globais com o nome da tabela.

  • Stub de autocomplete

  • Introdução de generate_model_stub() após geração de modelos, preparando arquivos .pyi para suporte a autocomplete em editores de código.

  • Listagem de modelos

  • Nova função list_models() que permite:

    • Listar todos os modelos carregados do banco de dados se houver uma conexão.
    • Listar modelos armazenados no cache .wbmodels/ se não houver conexão ativa, com descriptografia via cryptography.fernet.
  • Geração em lote de modelos

  • Nova função generate_all_models(conn) para introspecção automática de todas as tabelas (e opcionalmente views), com controle de verbosity e injeção em escopo.

  • Acesso seguro a modelos por nome

  • Função get_model_by_name(name) permite acesso direto ao modelo carregado a partir do _model_registry.

  • Criação de tabelas temporárias a partir de queryset

  • Adição da função create_temp_table_from_queryset(...) que permite gerar tabelas temporárias (TEMP TABLE) a partir de uma consulta ORM.

🔧 Aprimoramentos

  • Tratamento de exceções em FKs
  • Implementado try/except com mensagem de aviso amigável ao ignorar falhas na leitura de FKs.

  • Módulo do modelo explicitamente definido

  • Atributo __module__ dos modelos agora é definido como "wborm.core", garantindo rastreabilidade correta e suporte ao autocomplete.

  • Registro global de modelos

  • Modelos agora também são armazenados em _model_registry, além do _model_cache, permitindo recuperação nominal direta.

  • Melhoria no mapeamento de nomes

  • Os nomes de variáveis e atributos agora são tratados explicitamente como str(col["name"]) para evitar inconsistências.

  • Distribuição de importações

  • Organização mais clara dos imports, separando o uso de bibliotecas externas (cryptography, pickle) e módulos internos (wborm.registry, wborm.model_cache etc.).

🗑️ Remoções

  • Nenhuma funcionalidade foi removida. A nova versão mantém retrocompatibilidade total, ampliando as capacidades existentes.

⚠️ Observações Técnicas

  • Para o correto funcionamento do cache local, é necessário garantir que:
  • A chave de criptografia está acessível (get_or_create_key()).
  • O diretório .wbmodels/ esteja presente e com permissões adequadas.
  • A introspecção de FKs pode ser ignorada silenciosamente em caso de falhas, garantindo robustez mas ocultando erros mais sutis se não logado em detalhes.

📝 Introspect.py

🚀 Melhoria Robusta na Função get_foreign_keys()

🔍 Análise de Chaves Estrangeiras (FK) com Base em Índices

  • A consulta agora utiliza sysindexes para identificar corretamente as colunas envolvidas nos relacionamentos, tanto no lado da chave estrangeira quanto da chave primária.
  • Suporte a até 16 colunas de índice (usando part1 a part16), permitindo detectar relacionamentos compostos com múltiplas colunas.

🧠 Correções Semânticas e de Precisão

  • Substituição do join direto por r.tabid = fk.tabid para o uso de c.tabid = fk.tabid, garantindo precisão na tabela filha (fk).
  • Inclusão do filtro idx_fk.part1 IS NOT NULL AND idx_pk.part1 IS NOT NULL para garantir que apenas relacionamentos com colunas de índice reais sejam retornados, evitando registros corrompidos ou incompletos.

🧹 Tratamento com COALESCE

  • Uso de COALESCE(..., '') garante robustez ao comparar nomes de tabelas com possíveis valores nulos, evitando falhas silenciosas ou quebras por NULL.

🛠️ Outros Ajustes Técnicos

  • Indentação SQL melhorada para clareza e leitura.
  • Os aliases fk, pk, fc e pc permanecem os mesmos, mas agora baseados em um cruzamento mais preciso com os índices referenciados nas constraints.
  • Estrutura da query refatorada para melhor manutenção futura e consistência com bancos que seguem o padrão Informix para metadados.

Nenhuma alteração em introspect_table()

A função introspect_table(...) permanece inalterada entre as versões antiga e nova. Portanto:

  • Sem impacto em chamadas existentes.
  • Mesma estrutura de introspecção de colunas.

📝 Query.py

🚀 Novas Funcionalidades

🧠 Cache de Resultados de Consultas

  • Introduzido cache local de resultados (_query_result_cache) com TTL configurável (_cache_ttl = 60 segundos por padrão).
  • Nova função live() para desativar temporariamente o cache em uma consulta específica.
  • Verificação e reutilização de resultados cacheados em all() para otimizar desempenho.

🧾 Classe ResultSet com Métodos Avançados

  • Nova classe ResultSet herda de list e adiciona:
  • show(tablefmt="grid"): renderização de resultados com tabulate, com destaque de cor (verde para consulta ao vivo, azul para cache).
  • Suporte a colunas dinâmicas com detecção automática ou baseadas em select(...).
  • Remoção automática de colunas 100% vazias.

📊 Função pivot(...)

  • Novo método pivot(...) permite transformar a saída em uma visualização estilo tabela dinâmica, com indexação, colunas cruzadas e valores preenchidos.
  • Uso de tabulate, cores de terminal ANSI e performance otimizada.

🧪 Função show(...) no QuerySet

  • Método show() que simplesmente chama all().show(...) com suporte a formato tabular customizável.

🧩 Suporte a Subqueries via Join

  • Método join() agora aceita um QuerySet com alias definido por .as_temp_table(alias), permitindo joins com subqueries SQL completas.

🧪 Criação de Tabelas Temporárias Encadeada

  • Método create_temp_table(...) disponível diretamente em um QuerySet para gerar uma tabela temporária com base na query atual e retornar automaticamente o modelo associado.

🔧 Melhorias em Funcionalidades Existentes

  • filter(...) mais poderoso
  • Agora aceita expressões livres como strings (filter("age > 30")) e objetos ColumnExpr, além do tradicional filter(campo=valor) com escaping seguro de aspas simples.

  • join(...) com cláusula ON flexível

  • Agora aceita:

    • Lista/tupla de colunas para ON a.col = b.col.
    • Expressão simples ("user_id") assumindo igualdade padrão.
    • Expressão SQL customizada.
  • Criação explícita de instâncias

  • Método _create_instance_from_row(...) cria objetos com fallback direto ao __dict__ se o campo não estiver em _fields.

  • select(...) propaga campos para o ResultSet

  • Permite renderização mais precisa e ordenada de colunas.

  • Escape seguro em count()

  • Protege os valores de filtro contra SQL Injection com str(v).replace("'", "''").

🗑️ Remoções / Alterações Descontinuadas

  • A função preload(...) foi removida no novo código. A pré-carga de relações precisa ser reimplementada caso seja necessária.
  • A lógica heurística de FKs inversas em preload(...) (baseada em id ou <rel>_id) foi descontinuada.
  • O método all() agora retorna um ResultSet, em vez de uma lista simples.

🎨 Melhorias Visuais e de UX

  • Integração com colorama para colorir bordas de tabelas no terminal conforme origem dos dados (cache ou vivo).
  • Utilização de tabulate com diversos formatos para facilitar leitura em CLI.

⚙️ Internals e Técnicas

  • Inclusão de hashing (sha256) para cache de consultas baseado no texto SQL.
  • Identificação de subqueries exige ._as_temp_table_alias definido previamente para segurança.
  • Compatível com generate_model() atualizado para modelos temporários injetáveis.

📝 Core.py

🚀 Novas Funcionalidades

🧬 Suporte a métodos dinâmicos de QuerySet

  • Interceptação automática via __getattr__ na metaclasse ModelMeta:
  • Permite invocar qualquer método disponível no QuerySet diretamente pela classe modelo, como User.limit(10).show().
  • Exemplo: Model.join(...), Model.show(), Model.pivot(...), etc., mesmo sem declarar manualmente.

📦 Criação de Tabelas Temporárias

  • Novo método create_temp_table() (em dois níveis: instância e classe):
  • Gera automaticamente a estrutura SQL com base nos campos do modelo.
  • Usa CREATE TEMP TABLE ... com log visual via cprint.
  • Útil para joins intermediários e persistência temporária de resultados filtrados.

📊 Visualização Avançada: Model.show(...)

  • Exibe resultados no terminal com tabulate, até 50 registros por padrão (ou filtrado).
  • Renderiza com cores no terminal usando colorama:
  • Azul (🔵) se o modelo for proveniente de cache.
  • Verde (🟢) se for carregado dinamicamente.

🔁 Pivot Dinâmico via Model.pivot(...)

  • Geração de tabela dinâmica (pivot) diretamente pelo modelo, com suporte a filtros e agregações (aggfunc="count" por padrão).

🔍 Relações Modeladas Visivelmente: Model.describe_relations()

  • Lista todas as relações mapeadas automaticamente para o modelo, exibindo nome do relacionamento e tabela de destino.

🔧 Melhorias Significativas

  • Melhoria na introspecção de campos (ModelMeta):
  • Usa str(k) para garantir compatibilidade com chaves não-string (robustez com introspecção dinâmica ou subclasses especiais).

  • Refinamento em to_dict():

  • Agora ignora atributos que começam com _ ou que são métodos, gerando um dicionário limpo apenas com dados úteis.

  • Melhoria na criação de tabelas (temporárias e fixas):

  • Compactação da lógica de tipo SQL + flags NOT NULL e PRIMARY KEY com .strip() no final.
  • Código mais legível e reutilizável.

🗑️ Remoções / Alterações Descontinuadas

  • 📤 Removido o método show() de instância
  • Substituído por um show() de classe, mais útil e abrangente.

  • 📦 Método preload() do QuerySet ainda referenciado, mas provavelmente obsoleto (dependência que sumiu em QuerySet novo).

  • Pode ser removido futuramente se confirmado o desuso.

🎨 Melhorias de Interface e UX

  • Saída mais bonita com colorama para destaque de tabelas, relações e mensagens de criação.
  • Mensagens informativas ao criar tabelas (🧪 Tabela temporária criada: ...), deletar ou atualizar.

🛠️ Técnicas e Estruturais

  • lazy_property permanece como helper funcional, sem alterações.
  • validate() continua garantindo consistência e obrigatório antes de qualquer operação de persistência (add, bulk_add, etc.).

📝 __init__.py

🚀 Novos Recursos Exportados Publicamente

Foram adicionados novos utilitários à API pública da biblioteca, permitindo um uso mais expressivo e direto de expressões SQL e funções auxiliares:

📐 Expressões e Funções Utilitárias

  • col – Criação de expressões baseadas em colunas (ex: col("idade") > 18).
  • date – Conversão e manipulação de valores de data.
  • now – Data/hora atual para uso em filtros ou inserções.
  • raw – Inserção de SQL bruto diretamente em expressões.
  • format_informix_datetime – Utilitário para formatar datas no padrão aceito pelo Informix.

Esses recursos vêm do novo módulo expressions.py, agora incluído como parte do pacote principal.


🧭 Exportação Explícita com __all__

  • Adição do bloco __all__ = [...], tornando explícito o que é exportado ao usar from wborm import *, melhorando:
  • Legibilidade e manutenção.
  • Controle de namespace.
  • Completude para IDEs e documentação automática.

Retrocompatibilidade Mantida

  • Todos os elementos anteriormente exportados (Model, Field, generate_model, get_model, QuerySet) continuam disponíveis sem alterações.