Recentemente decidi conhecer o framework Django pois, de acordo com algumas discussões na lista do Rails, aquele era
mais adequado para o desenvolvimento de aplicações modulares. Como disse em outro
post, estou construindo uma aplicação web extensível (através de plugins e temas, por exemplo).
Meus testes com o Django duraram mais ou menos uma semana. Em primeiro lugar eu precisava ganhar um pouco de
conhecimento da linguagem sobre a qual o framework é construído: Python. Essa foi a parte mais fácil. Não porquê eu
a tenha considerado uma linguagem simples de aprender, e sim porquê eu não precisava aprender tanta coisa assim antes de
poder mergulhar no uso do framework.
Antes de começar a comparação, gostaria apenas de adiantar uma das conclusões: o tipo de modularidade que eu preciso,
que é a capacidade de escrever uma aplicação com componentes plugáveis (semelhante aos temas, plugins e widgets do
Wordpress) é bem diferente do conceito de modularidade pregado pelo Django (e isso eu só descobri ao longo do estudo).
O significado de modularidade para os desenvolvedores do Django é a facilidade para trocar componentes completos entre
aplicações. Isso ficará mais claro ao longo deste artigo.
Este artigo assume que você tem um entendimento superficial sobre a proposta do Rails, mas não do Django. As versão
analisadas são Rails 1.2 e Django 0.96.
A terminologia dos frameworks
As diferenças entre Rails e Django começam cedo, já na terminologia. Ambos são construídos sobre a arquitetura MVC. Mas
o que cada uma dessas letras significa em cada uma das abordagens?
O que há de comum nas terminologias:
- O M significa Model, modelo, e se traduz na biblioteca de software que abstrai o banco de dados da aplicação. Classes
são mapeadas para tabelas, e objetos são mapeados para registros.
O que muda:
- C (Controler)
- Em Rails, o Controller é uma classe cujos métodos são responsáveis por atender as requisições da aplicação. Cada
método, em geral, realiza funções tais como recuperar uma listagem de registros do banco de dados e deixar os
dados prontos para que um View os apresente.
- Os criadores do Django vêem o próprio framework como o Controller da aplicação, e entendem por View aquilo
que o Rails entende por Controller.
- V (View)
- Em Rails, um View é um trecho de código responsável por gerar saída HTML. Nenhuma operação de acesso ao banco de
dados deve ser realizada em um View (essas fazem parte do Controller). As variáveis inicializadas no Controller ficam
disponíveis para o respectivo View.
- O Django entende por Template aquilo que o Rails entende por View.
Resumindo, tem-se as seguintes relações:
- Rails Controller X O próprio framework Django
- Rails Model X Django Model
- Rails View X Django Template
Criando uma aplicação
Em Rails uma aplicação pode ser criada com um simples comando:
> rails nome_da_aplicacao
Em Django:
> django-admin.py startproject nome_da_aplicacao
Estrutura de diretórios
Uma vez criada a aplicação, acabamos com a seguinte estrutura de diretórios para a aplicação Rails (apenas os principais
arquivos foram listados):
app/
controllers/
helpers/
models/
views/
templates/
config/
routes.rb
database.yml
db/
script/
generate
test/
vendor/
Para o Django, a estrutura criada é bem mais enxuta:
__init__.py
manage.py
settings.py
urls.py
Enquanto o Rails cria uma grande árvore de arquivos, o Django cria apenas 4.
Durante a construção de uma aplicação Rails essa estrutura de diretórios, em geral, é mantida. O desenvolvedor faz uso
do programa script/generate para gerar Controllers, Models e Views (e seus respectivos testes).
No Django, ao contrário, o desenvolvedor acabará, obviamente, com uma estrutura de diretórios bem maior do que aquela
com a qual iniciou o projeto. Em geral cada nova funcionalidade demanda um novo diretório, pois essa é a filosofia do
Django: cada diretório da sua aplicação deve, em teoria, ter a capacidade de ser “plugado” em uma outra aplicação Django
e, com poucas configurações, continuar funcionando. É dessa forma que os desenvolvedores do Django entendem a palavra
modularidade: uma aplicação é construída a partir de componentes menores, os quais podem ser trocados entre
aplicações diferentes.
Processamento de requisições
Uma vez entendida a diferença de nomenclaturas entre os dois frameworks pode-se entender facilmente que, de forma geral,
o processamento de requisições ocorre da mesma maneira nos dois frameworks:
- O mapeamento entre URLs e o código-fonte (quais URLs invocam quais classes e métodos) fica em um arquivo de rotas
(Rails e Django).
- A lógica de negócio fica no Controller (Rails) ou em um View (Django)
- O acesso ao banco de dados fica encapsulado por uma biblioteca implementando o padrão Active Record (Rails e Django)
- A saída HTML é gerada dentro de Views (Rails) ou Templates (Django)
Roteamento de requisições
O roteamento de requisições, em ambos os frameworks, declara qual URL invoca qual ação em qual Controller. A grande
difereça aqui, que para mim favorece o Rails, é que o Django exige que façamos a configuração de todas essas rotas,
por mais simples e óbvias que possam ser.
Imagine a seguinte URL:
http://exemplo.com/users/view/1
O Rails utiliza um esquema default de mapeamento que deduz, a partir da URL acima, o seguinte:
- controller = users (será instanciada a classe UsersController)
- action = view (será invocado o método view nessa instância)
- id = 1 (dentro desse método estará disponível a variável local params[:id])
Para uma aplicação simples, esse esquema pode ser suficiente para todas as suas necessidades. Se não for, você pode
definir novas regras, que possuem prioridade dependente da ordem em que são declaradas.
Com o Django, por outro lado, todos os mapeamentos precisam ser definidos por meio de expressões regulares. Não é
complicado (graças à documentação), mas se podemos ter isso de graça no Rails, fica chato ter que voltar a se
preocupar com esse tipo de coisa dentro do Django.
Rails Controllers versus Django Views
Para comparar as duas abordagens vamos considerar que a nossa lógica de negócio envolve a manipulação de uma tabela
“users”. Estamos implementando a ação “list”, que deve preparar, para exibição, todos os registros dessa tabela.
Em Rails (no arquivo app/controllers/users_controller.rb):
class UsersControler < ActiveRecord::Base
def list
@user_pages, @users = paginate :users, :per_page => 10
end
end
Em Django (no arquivo nomedasub_aplicacao/views.py):
from django.shortcuts import render_to_response
def list(request):
users = User.objects.all()
return render_to_response('users/list.html', {'users': users})
Diferenças fundamentais:
- Um Rails Controller é definido como uma classe, enquanto um Django View é um conjunto de funções
- O arquivo que define o Rails Controller não costuma requerir outros arquivos ou pacotes. O arquivo que define as
funções de um Django View, por sua vez, costuma requerir um ou mais pacotes.
- Os métodos de um Rails Controller não recebem parâmetros vindos do framework. As funções de
um Django View sempre recebem um objeto
HttpRequest.
- Todas as variáveis que se deseja utilizar em um Rails View devem ser inicializadas, dentro do Rails Controller, como
variáveis de instância (
@users, no exemplo). Esse mesmo comportamento não pode ser alcançado com o Django: é
necessário passar as variáveis explicitamente para um Django Template, na forma de um dicionário Python (’nome da
variável’: valor). No exemplo acima é passado o dicionário {'users': users}.
- As convenções de Rails eliminam a necessidade de se declarar, dentro de um método do Rails Controller, qual View deve
renderizar os objetos carregados no Controller. No Django essa declaração é obrigatória. Em Rails, é claro, se você
precisar renderizar um View que não segue a convenção, você também pode fazê-lo facilmente.
Observações:
- O tutorial básico do Django não indica como inicializar, dentro do Django View, um objeto que encapsule a paginação
referente aos registros recuperados. Deve haver uma forma simples de fazer isso, mas fiquei impressionado por isso não
estar presente no tutorial.
Rails Views versus Django Templates
É uma preocupação comum aos dois frameworks, por serem adeptos do MVC, localizar a renderização de código HTML em um
arquivo distinto do arquivo que interage com o banco de dados. Esses arquivos possuem, em Rails, extensão .rb, enquanto
no Django possuem extensão .html. Isso pode ser um problema para certos editores de texto. Eu, por exemplo, trabalho no
editor Vim. Com alguns pacotes de configuração a coloração da sintaxe dos meus arquivos .rb fica perfeita. Já com
arquivos .html do Django eu não tive a mesma sorte.
Seguindo o exemplo deste artigo, um trecho do Rails View que lista os usuários poderia ser o seguinte:
<ul>
<% for user in @users do %>
<li><%= user.full_name %></li>
<% end %>
</ul>
E o mesmo trecho, para um Django Template:
<ul>
{% for user in users %}
<li>{{ user.full_name }}</li>
{% endfor %}
</ul>
Nos dois frameworks um view/template pode ser mais complexo do que isso, incluindo estruturas de desvio condicional, por exemplo.
Diferenças fundamentais:
Observações:
- Em Ruby, o código
user.full_name é válido, e significa invocar o método full_name do objeto user. Mas em Python, ao
contrário do que o exemplo acima dá a atender, a declaração user.full_name não invoca o método full_name de user. É o
sistema de templates do Django quem faz esse processamento. Primeiro o Django verifica se full_name é uma chave do
dicionário user (mesmo que user não seja um dicionário). Se não for, então verifica se é um atributo do objeto
user. Se não for, então invoca full_name como um método.
- No Django é possível utilizar um sistema de templates diferente do sistema provido pelo framework. Em Rails é possível
apresentar os templates em formato XML.
Rails Models versus Django Models
Tanto em Rails quanto em Django a camada de abstração do banco de dados implementa o mesmo padrão de projeto: Active
Record, proposto por Martin Fowler. Apesar disso, as duas implementações são bastante diferentes.
A implementação Ruby faz uso intenso de metaprogramação para, “automagicamente”, definir atributos e métodos dos Models.
Exemplo:
class User < ActiveRecord::Base
has_and_belongs_to_many :communities
end
No exemplo acima, declaramos a classe User, o nosso Model. Quando o código é executado, nossa classe ganha diversos
métodos e atributos, referentes a todas as colunas presentes na tabela “users” do banco de dados. A invocação do método
has_and_belongs_to_many também cria diversos métodos para a classe. Como todas essas definições são feitas em tempo de
execução, a performance fica prejudicada. É uma escolha que se faz: facilidade e agilidade para
definição de seus modelos em troca de uma performance um pouco inferior.
Mas há outra vantagem nessa abordagem: uma mudança no banco de dados não significa, obrigatoriamente, uma mudança na
definição da nossa classe. No início do projeto, quando ainda não se sabe quais campos nossa tabela deve ter, isso é uma
grande vantagem para o desenvolvimento. Essa filosofia de evitar repetições está presente por todo o Rails, e é
conhecida como DRY (Don’t Repeat Yourself, ou “Não se repita”).
A abordagem do Django é inversa: o nosso Model é quem diz qual será a estrutura da nossa tabela. Um exemplo:
from django.db import models
class User(models.Model) # Em Python isso significa que User extende models.Model
first_name = models.CharField(max_length=60)
last_name = models.CharField(max_length=60)
Quando queremos que o banco de dados se modifique, precisamos rodar um comando do Django que processa as definições de
modelos e cria as respectivas tabelas (se elas não existirem):
> python manage.py syncdb
A performance da implementação Django para o Active Record é, em teoria, superior a do Ruby, uma vez que as classes não
são definidas por metaprogramação. Isso pode ser um fator determinante para aqueles que necessitam de alto desempenho
(embora isso varie muito dependendo da configuração do seu servidor - Apache servindo em modo mod_python ou fastcgi,
por exemplo).
Observações:
* No Django é possível utilizar uma outra camada de abstração de banco de dados que não essa apresentada anteriormente.
Segundo a documentação essa troca é feita de maneira trivial. Já no Rails, tentar utilizar uma outra biblioteca para
isso provavelmente não vale a pena, pelo trabalho que daria. Mas será que alguém realmente não gosta da ActiveRecord
do Ruby?
Evolução do banco de dados
No Rails, controlar a evolução do banco de dados é muito simples (comparado com Django): cada modificação na estrutura
do banco de dados demanda a criação de uma nova migração. Imagine, por exemplo, que o nosso sistema já está em produção
e o cliente nos diz que ele precisa de mais uma informação a respeito do usuário: sua data de nascimento.
Com o seguinte comando criamos uma nova migração:
> ruby script/generate migration add_birthday_to_users
Esse comando gera o arquivo db/migrate/???_add_birthday_to_users, onde ??? é o número dessa migração (o Rails o controla
automaticamente). Abrindo esse arquivo editamos o código Ruby que realiza a nossa tarefa. Supondo que estejamos
trabalhando com controle de versão de software, fazemos o commit da nossa modificação e em seguida vamos até o servidor
de produção para aplicar a mesma modificação nesse banco de dados. Atualizamos o código da aplicação (com “svn up”, por
exemplo), e em seguida executamos:
> rake db:migrate
Esse comando irá trazer o banco de dados para a última versão disponível.
Agora você me pergunta: e como faço essa mesma operação no Django? E eu te responderei: faça tudo sozinho, em SQL puro.
É sério. No Django você controla a evolução do seu banco de dados manualmente. Para o exemplo acima isso pode até não
ser um problema, pois nossa modificação era trivial. Agora considere modificações maiores, que envolve transferências de
dados entre tabelas. E se sua aplicação for independente de banco de dados? Bem, você terá que escrever o SQL específico
de cada SGBD.
Esse é um dos fatores que, na minha opinião, conta muito contra o Django, em sua versão 0.96.
Testes de software (TDD e BDD)
Este tópico pode ser irrelevante para quem não aplica desenvolvimento dirigido por testes, ou simplesmente teste de
software. Se você se inclui nesse grupo, então não ficará triste por saber que não há uma forma muito simples de seguir essa metodologia
no Django (ao menos o tutorial de iniciação no framework não dá nenhuma dica nesse respeito), e buscas pelo Google não
me ajudaram muito também.
No Rails as opções são várias: você pode utilizar a biblioteca fornecida pelo próprio Rails, Selenium, RSpec (que eu
utilizo e recomendo), entre outras. RSpec, por exemplo, é integrado ao Rails por meio de um plugin, muito
fácil de instalar e de começar a utilizar.
A falta de TDD e BDD integrado ao Django é, na minha opinião, mais um fator que conta contra o Dango.
Particularidades do Django
Uma das características que os desenvolvedores do Django mais exaltam no framework é a interface administrativa criada
“automagicamente” para os Models definidos por você. Essa interface é realmente poderosa, pois é capaz de manipular
relacionamentos 1-N que o scaffold básico do Rails ignora. Para colunas que representam datas você ganha até caléndários
Ajax para definir os valores.
No entanto, o visual dessa interface administrativa dificilmente será aquilo que você realmente deseja para a sua
aplicação. De qualquer modo, melhor tê-la do que não tê-la.
Para o Rails há um plugin que reproduz funcionalidade semelhante: Auto Admin (o
site estava fora do ar quando acessei pela última vez).
Livros sobre o assunto
Buscando por “rails” no site da Amazon encontrei em torno de 20 livros sobre o assunto. Já sobre Django, encontrei apenas 2.
A situação do Django pode mudar nos próximos meses, principalmente se a versão 1.0 do Django for lançada.
A documentação do Django é muito boa, e pode ser acessada aqui. No
momento, no entanto, a documentação do Rails é incomparável, com uma grande quantidade de tutoriais espalhados pela
internet, blogs sobre o assunto e muitos livros disponíveis.
Conclusões
Este artigo teve por objetivo dar uma visão geral das principais diferenças entre a utilização do Rails e do Django em um
projeto. Quando comecei a estudar o Django estava disposto a deixar o Rails de lado se o primeiro realmente se provasse
mais adequado para o tipo de aplicação que desejo fazer. Mas, no fim das contas, concluí que nenhuma delas facilita a
minha vida nesse sentido.
Bom, se nenhuma delas me ajuda no desenvolvimento de uma aplicação extensível, então qual o meu critério de escolha? A
que me dá mais vantagens durante o desenvolvimento (mas outras pessoas podem escolher performance, deployment,
familiaridade com a linguagem etc.). Rails, nesse sentido, está anos luz à frente. A definição de tabelas utilizando
sintaxe Ruby, a definição de modelos de forma DRY, o controle da evolução do banco de dados de forma trivial, os testes
com RSpec facilmente integrados à aplicação…
Se você é um programador Python experiente, recomendo que use o framework por umas semanas e tire suas próprias
conclusões. Você pode se sentir muito à vontade com ele e não sentir falta dessas coisas que listei.
Se você é um programador Ruby, no entanto, recomendo que continue com o Rails. Mas essa é apenas a minha opinião.
Referências
Atualização 1 (03/11/2007): alguns trechos de código-fonte tinham problemas (apontados pelo Marcelo Minholi
nos comentários). Verifique também os comentários de Andrews Medina para conhecer mais sobre as opções
do Django para TDD, que não foram abordadas no artigo por não estarem explícitas na documentação oficial
na época em que esta comparação foi escrita.