Herança Modular

A minha maior preocupação na hora de implementar a camada ORM, foi com a herança de Models. Desenvolver um software ERP/CRM sem um recurso como esse, é fazer mais do mesmo.

Como expliquei em postagens anteriores, o OpenERP e Django forneceram ideias bem interessantes de como escrever aplicações modulares que permitam extensões de Models. Algumas linguagens, como X++ da Microsoft por exemplo, possuem um ambiente próprio que integra muito bem os recursos da linguagem, simplificando bastante a escrita de códigos voltados para a construção de regra de negócios e manipulação de dados. Em todo caso, Python possui recursos de linguagem que nos permitem simular certos comportamentos fazendo uso de meta-programação.

Com a meta-programação podemos modificar a forma como instâncias de objetos e até instâncias de tipos (como classes) são criados, ou como membros/atributos de objetos são lidos ou escritos, isto nos permite mudar o comportamento natural dos objetos, como vamos ver a seguir:

Exemplo de Herança Simples (Extensão)

class Pessoa(models.Model):
    nome = models.CharField()


# Adiciona o campo cpf na tabela pessoa
class PessoaFisica(Pessoa):
    cpf = models.CharField()


# Adiciona o campo ativo na tabela pessoa
class Funcionario(Pessoa):
    ativo = models.BooleanField(default=True)

O que vemos no código acima é um exemplo de herança simples, por padrão, quando um Model é estendido pela sua classe derivada, os novos campos são adicionados à classe ancestral (inclusive sua tabela), caso o campo já exista, seus atributos são substituídos pelos novos atributos.

Este recurso é indispensável para a escrita de aplicações modulares: Imagine que sua você pretende escrever uma aplicação modular, nesta aplicação você terá 2 módulos, 1 módulo que funcionaria com recursos básicos para os usuários os usuários “basic”, e um outro módulos com recursos adicionais/avançados para os usuários “premium” do produto que pretende desenvolver. Neste cenário o desenvolver pode tirar proveito do recurso de herança modular e adicionar novos recursos aos módulos existentes.

Exemplo de Herança de Classes Abstratas

class Pessoa(models.Model):
    nome = models.CharField()
    class Meta:
        abstract = True


class Aluno(Pessoa):
    media = models.FloatField()


class Professor(Pessoa):
    disciplina = models.CharField()

Neste caso o Model Pessoa é abstrato, o que indica que ele só poderá ser usado quando derivado. O Model Aluno herda o campo nome, e adicionado media, sua tabela então ficará com o campo “nome” e “media”, de modo que o Model Professor adiciona disciplina e nome na tabela “professor”. A tabela “pessoa” não é criada por possuir característica abstrata.

Herança Concreta (Prevenindo Extensões)

Existem casos que precisamos criar heranças em que os dados existam numa tabela, e seus dados adicionais em outro Model. Neste caso a herança é realizada através de um campo OneToOneField, que é uma chave primária, e estrangeira para seu Model ancestral, exemplo:

class Pessoa(models.Model):
    nome = models.CharField()


class Aluno(Pessoa):
    media = models.FloatField()

    class Meta:
        extension = False


class Professor(Pessoa):
    disciplina = models.CharField()

    class Meta:
        extension = False

No exemplo acima as tabelas “pessoa”, “aluno” e “professor” são criadas, a tabela “aluno” possui apenas “id” e “nome”, enquanto que a tabela “aluno” terá os campos “pessoa_ptr_id” e “media”, e a tabela “professor” possuirá “pessoa_ptr_id”, “disciplina”. O campo “pessoa_ptr_id” um campo OneToOneField que é tanto chave primária, como chave estrangeira para a tabela pessoa, internamente o ORM age carregando os dados dos Models hierarquicamente de forma automática.

Criando relatórios incríveis com Python + FastReport.Net

Já faz algum tempo escrevi como criar relatórios usando as libs FastReport e FreeReport do Delphi e integrando com Python. Desta vez vou mostrar uma maneira bem simples de gerar relatórios realmente profissionais usando Python e FastReport.Net.

Os desenvolvedores mais experientes costumam desenvolver seus reports usando templates HTML, outros usam wkhtmltopdf para converter o HTML gerado em PDF, usando técnicas bastante simples de linha comando. Também existem ferramentas como Birt e outros. A alternativa que apresento aqui é para demonstrar como gerar relatórios com uma ferramenta profissional de alta performance que faz muito sucesso entre os desenvolvedores C#.

Apesar do FastReport.Net ser um produto comercial, seu preço é absolutamente accessível, e o resultado vale muito a pena. O objetivo deste post não é ensinar a usar o FastReport, mas sim de como usá-lo em Python.

Antes de começarmos é necessário baixar e instalar o FastReport, e uma pequena lib de integração que eu mesmo escrevi em C++/CLR, esta lib é necessária por fazer a comunicação entre o FastReport no ambiente .Net e o ambiente CPython. A lib está disponível em: https://github.com/katrid/python-fastreport

Vamos ao exemplo:

import fastreport

def test_report():
    fastreport.show_report('test.frx', 'test.pdf', 'pdf')

if __name__ == '__main__':
    test_report()

Neste caso vamos renderizar o report test.frx em test.pdf usando o formato PDF. A função show_report é responsável por invocar a lib FastReport e exportar para o formato informado.

Orun Framework (Diário)

Apesar de estar codificando bastante, faz tempo que não posto informações atualizadas sobre o projeto Orun.
Ultimamente já tenho utilizado o Orun em produção (ainda não recomendo para outros desenvolvedores), as dificuldades foram todas superadas, o ambiente já começa a se mostrar bastante eficiente, atendendo até mesmo demandas minha de aplicações não corporativas, como uma API de anúncios compartilhados que desenvolvi, o que diferencia bastante o Orun de um Framework como OpenERP por exemplo. Exatamente como imaginei.

Neste post vou tentar explicar as dificuldades encontradas e como as superei:

Múltiplos apps na mesma instância

Executar múltiples apps, cada um com suas configuração, com sua base, arquivos estáticos, etc… Seria um grande obstáculo com muitos web framworks, principalmente django, uma vez que seu uso é específico para single instance e single app. Esse problema foi facilmente superado graças ao Flask (mais uma vantagem desse incrível framework), exemplo de configuração de múltiplos apps na mesma instância:

from orun.apps import factory
class SubdomainDispatcher(factory.SubdomainDispatcher):
    def create_app(self, instance_name):
        inst = super(SubdomainDispatcher, self).create_app(instance_name)

    def create_settings(self, instance_name):
        settings = super(SubdomainDispatcher, self).create_settings(instance_name)
        settings['ADDONS'] = MODULES
        url = make_url(conn_str)
        url.database = instance_name
        settings['DATABASES']['default']['ENGINE'] = str(url)
        return settings

Essa solução é bastante útil quando o desenvolvedor pretende que múltiplos databases rodem na mesma instância/processo de forma dinâmica, sob demanda, à medida que novos usuários vão se conectando, o servidor instancia objetos Flask e redireciona os requests.

Internacionalização

Técnicas modernas de tradução e internacionalização ajudam muito na hora de escrever apps que rodem em múltiplos países.

Superei este problema ajustando o código existente no django em django.utils.translation, a lib do django já estava muito próxima de atender as necessidades do framework, exemplo de uso:

from unittest import TestCase
from orun import app
from orun.utils.translation import gettext


class TranslationTestCase(TestCase):
    def test_trans(self):
        self.assertEqual(gettext('User'), 'User')
        with app.app_context(LANGUAGE_CODE='pt-br'):
            self.assertEqual(gettext('User'), 'Usuário')

            with app.app_context():
                self.assertEqual(gettext('User'), 'Usuário')

                with app.app_context(LANGUAGE_CODE='en-us'):
                    self.assertEqual(gettext('User'), 'User')
                self.assertEqual(gettext('User'), 'Usuário')

            self.assertEqual(gettext('User'), 'Usuário')
        self.assertEqual(gettext('User'), 'User')

No test case acima demonstra que combinei dois recursos excelentes durante o desenvolvimento do framework, o recurso de traduções do django, e o app context do Flask. Assim o desenvolvedor pode mudar o idioma do contexto sempre que for necessário.

Resumindo

Até aqui, exceto pelo ORM, o desenvolvimento tem sido tranquilo, as muitas dificuldades foram superadas com a simples adaptação de recursos já existentes no Flask ou no Django.

Keops Project

O Keops Project nasceu da tentativa de permitir que o Django suportasse o desenvolvimento de Business Apps, ERP, CRM, etc. Como mencionei anteriormente, depois de algumas tentativas de sanar obstáculos existentes, percebi que seria necessário algum tipo de desconstrução pesada, uma vez que o Django não foi construído para suportar casos bem específicos de sistemas modulares corporativos (abordarei aqui esse tema superficialmente, uma vez foi melhor abordado em postagem anterior).

O Keops foi nascendo a medida que fui retirando aspectos essências para construção de sistemas modulares extensíveis. O resultado acabou me chamando atenção por permitir a escrita de excelentes painéis administrativos para projetos Django, de forma semelhante ao que temos no Admin padrão. Podendo-se dizer inclusive que o Keops é uma poderosa alternativa ao Admin, que apesar de ainda estar em status alpha (em desenvolvimento), já oferece recursos modernos essenciais para o mercado web, como:

  • Criação simplificada de APIs
  • Client-side com tecnologia AngularJS v 1.5
  • Controle de permissão em nível de menu
  • Forms com tecnologia Ajax
  • Forms customizáveis (jinja2 templates por models, apps e geral)
  • Painéis administrativos customizáveis (jinja2 template)

O processo de separação gerou 1 Framework adicional, o Framework Katrid (https://github.com/katrid/katrid), que apesar de transparente para o Keops, é essencial para construção de aplicações que rodam no client-side. Este Framework será melhor abordado em breve, pois ele agrada a “gregos e troianos”, por permitir que outras tecnologias e linguagens integrem com seu ambiente de forma bastante simplificada.

O forms possuem uma especificação diferenciada do html 5 convencional. Por possuir tags “shortcuts”, o processo de escrita de forms CRUD foi intensamente simplificado. A especificação foi inspirada na mesma especificação disponível no OpenERP/Odoo, e segue bem o princípio DRY do Django, ex:
<form><field name="name"/><field name="description"/></form>

No exemplo acima somente os campos name e description serão exibidos no formulário, as tags são substituídas por widgets pré-configurados no Framwork Katrid.

Continuo em breve.

Orun Framework

Retomo os meus posts, desta vez trazendo novidades sobre o desenvolvimento do nosso Framework Keops.

Quando iniciei o desenvolvimento do Framework Keops (por volta de 2009), era a necessidade de alguma ferramenta que realmente ajudasse a acelerar o desenvolvimento de aplicações empresariais. Apesar do framework ser utilizado desde sua concepção alpha em alguns clientes, nunca recomendei, e ainda não recomendo seu uso em produção, uma vez que ainda há muito por ser feito.

Naquela época, iniciei estendendo o Django e usando o ExtJS como camada de apresentação. O objetivo fundamental do projeto era:

  • Modularização (Permitir que desenvolvedores criem seus próprios módulos)
  • Fácil codificação (por isso escolhi python)
  • ORM (codificar totalmente, ou o máximo possível, sem necessidade de SQL direto)
  • SQL Direto (quando não for possível usar o ORM, permitir o uso de RAW SQL)
  • Herança de Modelos
  • Comunicação com o client/server através de uma API simples através de HTTP Request
  • Apresentação através da web com html5, css3 e Javascript
  • Comercialmente viável (permitir que desenvolvedores e clientes integrem seus aplicativos com libs comerciais)

Resumindo, desenvolver algo tão prático quanto o OpenERP (agora Odoo), e tão decente quanto o Django.

Antes de continuarmos, que tal uma breve comparação entre o melhor dos 2 Django e Odoo (ponto de vista ERP e CRM):

Prós Django

  • ORM fantástico (facilidade de abstração)
  • Código limpo
  • Bem estruturado
  • Database Backend (novos drivers)
  • Fácil codificação
  • Fácil internacionalização
  • Lógica de funcionamento simples
  • Incrível plataforma de testes
  • Migrations

Contras Django:

  • Modularidade pobre
  • Aplicações monolíticas
  • Herança limitada dos modelos (permite apenas heranças criando novos objetos de banco de dados ou proxies, que não adiciona campos… até tentei abrir um ticket tentando enviar um patch com a solução para esta limitação, e se me recordo bem, eles não aceitaram alegando que não era o objetivo do Django)

Prós OpenERP

  • Permite Meta-Herança de modelos (muito bom, apenas fora dos padrões _inherit?)
  • Extensão de módulos
  • Aplicações versáteis
  • Incríveis views herdadas (uso do xpath)
  • Incríveis ideias para acelerar a produtividade do usuário (muitas das ideias são comumente encontradas nos ERP’s profissionais existentes: SAP, Dynamics…)

Contras OpenERP

  • ORM pobre
  • Código pobre
  • Difícil codificação
  • Somente PostgreSQL (não permite novos drivers)
  • Confusão entre False e None
  • Funcionalidades sem lógica, ou não funcionam como deveriam
  • Internacionalização somente fazendo carga no banco de dados

Obs:

Apesar da nova Odoo API 9.0, ter melhorado bastante a camada de codificação, ainda não vi vantagens suficientes para me fazer aproveitar o core.

Ora, mas o que quero dizer com decente?

Antes que muitos gritem, que eu não acho o OpenERP decente, eu acho sim, só quem conhece o código-fonte do OpenERP, entenderia o motivo de não considerar aquele código utilizável, apesar de conter um pacote de ideias incríveis.

Em contrapartida eu reafirmo, com meus quase 18 anos de experiência em desenvolvimento de softwares, o Django, é hoje, na minha opinião, sem dúvida, de longe, o melhor Framework para desenvolvimento Web disponível.

Já o Odoo apresenta estranhezas, tentei até mesmo ajustar, e melhorar o que já existe dentro código-fonte dele. Percebi que seria mais viável fazer isso com o Django, modificar o Django para que funcione voltado ao desenvolvimento de Aplicações Empresarias (ERP’s, CRM’s etc…).

Adaptando o Django… Isso foi bem chato… Veja bem, o Django é perfeito para construção de Web Portals, Web Apps, etc… Mas não necessariamente o acesso ao tipo de aplicações que eu deseja facilitar. Encontrei principalmente obstáculos no ORM, que apesar de excelente para o que se propõe, não permite extensões. Em outras palavras, o objetivo principal de um ERP, modularização, seria parcialmente atendido usando o Framework Atual do Django.

Neste caso, o Odoo sai na frente, o framework é bem versátil, permite extensões através de herança emulada através de meta-programação. Como superado anteriormente (não voltaremos ao assunto do motivo de não usar o Odoo), sigamos enfrente identificando mais dificuldades.

Já em 2014, percebi que me custaria muito tempo desconstruir certas partes do Django, e que seria mais fácil utilizar algumas libs externas para acelerar, foi o caso principalmente do Flask (meu segundo favorito).

Criei um novo repositório, com o codinome Orun (Object Runtime) somente para ser um App Server e um segundo projeto chamado Katrid, para ser uma camada para construção de Admin Panels usando javascript com AngularJS

Eu já conhecia o Flask, tendo inclusive desenvolvido alguns projetos para clientes. Achei um framework incrível, não tão completo quanto o Django, mas faz muito bem o que se propõe, além do mais possui ótimos recursos para quem pretende desenvolver aplicações modulares, como por exemplo os Blueprints.

Blueprints serviriam muito bem para acomodar os Add-ons do nosso Framework, são equivalentes aos AppConfig’s do Django eles já possuem por natureza configurações como diretório de templates e outras configurações que me fizeram poupar algum tempo.

As views solucionei bem com o uso do Jinja2, e uma implementação do xpath, como no OpenERP ajuda bastante, a criar herança de views. Apesar do Django Template ser muito bom, foi muito prático colocar Jinja2 pra funcionar, posso afirmar tranquilamente que foi a decisão mais acertada.

A pior parte SEM DÚVIDA, foram, ainda são, as mudanças no ORM, principalmente o comando makemigrations e migrate.

A herança de Models ficou bem padronizada, bem no estilo python, como a seguir:

class Company(Partner):

company_field = models.CharField()

 

Neste caso ao invés de criar uma nova tabela para o modelo Company, como o Django faz, o Orun adiciona os campos adicionais na tabela Partner.

Muitos podem questionar esse approach, mas é o método mais eficiente para construção de extensão entre módulos, é possível como visto, criar módulos herdando características de módulos diferentes, em novos módulos, o sistema consegue até mesmo detectar a ordem dos migrations.

Esse tipo de herança nos leva até mesmo a modificar campos existentes. Também por este motivo, elegi o Flask como camada servidora, ele permite múltiplas instâncias de aplicações wsgi no mesmo servidor, assim podemos ter vários modelos com o mesmo nome, e com diferentes estruturas construídas a partir dos módulos instalados em uma determinada instância.

Resumo

Com o passar dos anos temos visto muitas novas tecnologias para facilitar o desenvolvimento de aplicações empresariais, com os novos modelos de negócios sistemas antigos devem dar espaço aos novos e robustos sistemas de gestão através da web.

Pensando em evolução resolvi criar o projeto Keops que deu origem ao projeto Orun, que tenho o orgulho de afirmar que tomei as decisões assertivas quando combinei Flask e Django, criando um projeto derivado do Django e transformando-o num poderoso Business Framework.

Orun on github: Orun Project