Quem tem medo do admin do Django?
Este post não será mais um post sobre o porquê você não deve usar o admin do Django.
Vamos falar sobre alguns conceitos que uma aplicação web envolve e como o admin interage com eles. Além disso, a internet já está repleta de posts sobre a polêmica ao redor do admin e, honestamente, ainda não tenho uma opinião definida sobre o assunto. Principalmente porque acredito que o vilão não é o admin do Django, mas sim o código que você cria.
Quando penso em uma aplicação web, penso em três coisas: dados, estados e fluxos. Os dados compõem a parte mais simples de entender, eles são basicamente as informações que foram persistidas pela aplicação. Pensando no caso de um sistema de vendas, os dados seriam as informações de preço, quantidade e dimensões de um produto, por exemplo. Os estados seriam leituras e avaliações que se aplicam sobre um conjunto de dados e que são importantes para o domínio da aplicação. No nosso exemplo podemos pensar que a definição de se um produto está apto ou não para a venda seriam estados. Por fim, os fluxos são as interações internas da aplicação que têm por objetivo manipular seus dados para levá-los de um estado para o outro.
Essas naturezas e momentos distintos são o que podem transformar o admin do Django no seu melhor amigo ou no seu pior pesadelo. Isso se dá pelo fato de que o admin, na sua versão out of the box, lida apenas com um único aspecto das aplicações web: a manipulação direta dados. Essa característica parece inofensiva num primeiro momento, mas é algo que pode influenciar negativamente o código da sua aplicação. É ela que facilita a vida de desenvolvedores e gestores do sistema quando precisam analisar os dados, mas é ela, também, que pode quebrar uma aplicação quando mal utilizada por algum usuário final que não saiba das regras que definem os estados possíveis da aplicação.
Como um exemplo rápido, vamos pensar numa ordem de envio de produtos. Vamos supor que a ordem de envio seja composta de um atributo que a define pronta ou não para o envio e uma relação de muitos para muitos com envio de produtos que também possuem esse atributo. No momento de processamento da ordem, o sistema checa se todos os envios estão pronto e, em caso afirmativo, a ordem é marcada como pronta. Agora, vamos pensar que uma ordem de envio foi processada e considerada apta pro envio, mas um dos produtos ainda não estava em estoque. Bem, do ponto de vista do admin, a solução é simples: é só atualizar o envio daquele produto como inapto, certo?
Do ponto de vista de banco de dados, sim, mas do ponto de vista do domínio da aplicação, não. Apesar ter sido atualizado como não apto, a ordem ao qual ele pertence e que já foi processada, continuará marcada como apta para o envio. Esse cenário é apenas um exemplo dentre vários outros que permitem a criação de um estado inválido dentro da aplicação. Estes estados não são contemplados pelo domínio e tratados pelos seus fluxos, portanto, a existência deles é o que, na maioria das vezes, gera bugs e/ou reclamações do cliente. Possibilitar estados inválidos é sempre uma questão a se ter com o admin do Django e isso se dá por ele ser diretamente acoplado ao seu banco de dados através do ORM do Django. Por padrão ele ignora todos os fluxos que você define na sua aplicação e, por consequência, pode violar a máquina de estados contida por eles.
Mas, como falei no início do texto, isso não é um problema unicamente do admin, mas também do seu código. Esse mesmo problema de permitir estados inválidos pode ser reproduzido em views criadas pelo desenvolvedor. Uma validação de um campo pode ter sido esquecida ou um objeto pode estar sendo modificado sem considerar suas dependências. Isso acontece principalmente em aplicações que restringem sua arquitetura ao básico Model, View e Template (ou Serializer) do Django. Nesse caso, a diferença entre uma View que valida um post e atualiza um Model diretamente e o admin do Django é a de que o admin já te dá a View de graça.
O problema, no final das contas, não está no admin, mas sim em ter um código que não limita as alterações de estados da aplicação como responsabilidades exclusivas dos seus fluxos. Em termos de arquitetura, eles podem ser implementados de várias maneiras, desde os Casos de Uso do Domain Driven Design até ao padrão de projeto Facade. Mas, independente da implementação, todos têm um objetivo em comum: explicitar os fluxos de execução da aplicação para, através de definição de responsabilidades e encapsulamento, inviabilizar que estados inválidos sejam criados através da manipulação direta dos dados.
Desta maneira, penso no admin como sendo apenas uma interface diferente das minhas Views que precisa interagir com os fluxos da aplicação com a qual eu preciso ser mais precavido. O lado bom é que ele disponibiliza uma API simples que me permite bloquear os campos somente para a leitura, implementar minhas Views do admin, ações customizáveis e customizar a deleção e criação de objetos. Utilizado dessa maneira, o admin deixa de ser invasivo e passa a ser somente uma outra maneira, ainda que limitada, de servir parte da sua aplicação.
Você pode também argumentar que outra possível maneira de limitar os efeitos colaterais do admin seria utilizar os sinais de post_save, post_delete etc para controle do fluxo. Para mim essa definitivamente não é uma solução ideal. Por que? Bem, isso é assunto para um próximo post…
Um outro calcanhar de Aquiles do admin aparece quando precisamos customizar a sua aparência para atingirmos uma melhor UX. Também por conta do seu acoplamento com o banco de dados, a sua interface é muito limitada e burocrática e, por mais que existam diversas lib que customizam o seu tema, ainda é difícil estender a sua apresentação, apesar de ser possível de implementar de uma maneira não invasiva. Mas esse é um papo para uma próxima postagem mais técnica sobre o admin.
E você, o que acha do admin do Django? Tem utilizado ele em projetos? Se sim, como e quais os problemas que enfrentou? Se não, por que?