O Python é uma linguagem de programação orientada a objetos de alto nível, e com grau de abstração relativamente elevado, longe do código de máquina e mais próximo à linguagem humana. Esta linguagem foi criada por Guido Van Rossum em 1991.
Hoje o desenvolvimento é comunitário e aberto com a ONG Python Software Fundation como gestor do projeto.
O mundo dos frameworks de aplicações web em Python está cheio de escolhas. Django, Flask, Pyramid, Tornado, Bottle, Diesel, Pecan, Falcon e muitos mais estão competindo pelo mindshare dos desenvolvedores. Neste artigo, focaremos apenas nos três primeiros.
Para ajudar a fazer a escolha entre os três, vamos construir a mesma aplicação em cada framework e comparar o código, destacando os pontos fortes e fracos de cada abordagem.
Se você chegou até aqui, mas gostaria de entender mais sobre Python antes de se aprofundar em seus principais frameworks, a CBSI separou 69 videoaulas gratuitas sobre Python, além de uma apostila gratuita para download.
Os frameworks
Flask é um “microframework” destinado, principalmente, a pequenas aplicações com requisitos mais simples. Pyramid e Django são ambos destinados a aplicações maiores, mas têm abordagens diferentes para a extensibilidade e flexibilidade. Pyramid prioriza a flexibilidade e permite que o desenvolvedor use as ferramentas certas para o seu projeto. Isso significa que o desenvolvedor pode escolher o banco de dados, estrutura de URL, estilo de modelo e muito mais. O Django inclui todas as baterias que uma aplicação web precisa para que os desenvolvedores precisem apenas abrir a caixa e começar a trabalhar, puxando os muitos módulos do Django à medida que desenvolvem.
Abaixo, você pode ver como KleberCode, um streamer brasileiro, desenvolve seu site em Django:
Django inclui um mapeamento objeto-relacional (ORM), enquanto Pyramid e Flask deixam que o desenvolvedor escolha como (ou se) eles querem seus dados armazenados. O ORM mais popular para aplicações web não-Django é SQLAlchemy de longe, mas há muitas outras opções desde DynamoDB e MongoDB. Para persistência local simples, costuma-se usar o LevelDB ou SQLite. Pyramid foi projetado para usar qualquer camada de persistência.
A abordagem de “baterias incluídas” do Django facilita os desenvolvedores que já conhecem o Python em mergulhar rapidamente nas aplicações web sem precisar tomar muitas decisões sobre a infra-estrutura antes do tempo. Django já tem embutidos formulários, roteamentos, autenticação, templates e administração básica de banco de dados. Em contraste, Pyramid inclui roteamento e autenticação, mas templates e administração de banco de dados exigem bibliotecas externas.
O trabalho extra em escolher componentes para aplicações em Flask e Pyramid produz mais flexibilidade para desenvolvedores precisam interoperar com diferentes fluxos de trabalho ou sistemas de templates.
Flask, o mais novo dos três, começou em meados de 2010. O framework Pyramid começou a vida no projeto Pylons e recebeu o nome de Pyramid no final de 2010, embora o primeiro lançamento tenha sido em 2005. Django teve seu primeiro lançamento em 2006, logo após o início do projeto Pylons (eventualmente Pyramid). Pyramid e Django são estruturas extremamente maduras, e têm acumulado plugins e extensões para atender a uma gama incrivelmente grande de necessidades.
Embora o Flask tenha uma história mais curta, foi capaz de aprender com os quadros que vieram antes e fixou-se firmemente em projetos pequenos. É claramente usado com mais freqüência em projetos menores com apenas uma ou duas funções. Um desses projetos é o httpbin, um auxiliar simples (mas extremamente poderoso) para depurar e testar bibliotecas HTTP.
As Comunidades
O prêmio para a comunidade mais ativa vai para o Django, que já conta com mais de 80.000 perguntas no StackOverflow, além de uma grande quantidade de blogs dedicados a este framework. As comunidades de Flask e Pyramid não são tão grandes, mas suas comunidades são bastante ativas em suas listas de discussão e no IRC. No Github, eles têm um número muito parecido de estrelas com 11.300 para o Django e 10.900 para o Flask.
Todos os três frameworks estão disponíveis sob licenças permissivas derivadas do BSD. As licenças do Flask e do Django são de 3 cláusulas BSD, enquanto a licença RPL do Pyramid é um derivado da licença BSD de 4 cláusulas.
Bootstrapping
Django e Pyramid vêm com ferramentas de bootstrapping inclusas. O Flask não tem nada do tipo porque o alvo Flask não é construir grandes aplicações MVC.
1. Flask
O aplicativo Hello World do Flask é o mais simples, sendo feito em meras 7 linhas de código em um único arquivo Python.
1 2 3 4 5 6 7 8 9 10 |
# from http://flask.pocoo.org/ tutorial from flask import Flask app = Flask(__name__) @app.route("/") # take note of this decorator syntax, it's a common pattern def hello(): return "Hello World!" if __name__ == "__main__": app.run() |
É por isso que não há ferramentas de bootstrapping para o Flask. Para projetos que precisam de mais separação entre os componentes, o Flask tem blueprints. Por exemplo, você poderia estruturar seu aplicativo Flask com todas as funções relacionadas ao usuário em users.py e suas funções relacionadas a vendas no ecommerce.py. Em seguida, importe-as e adicione-as ao seu app em site.py. Não vamos examinar esta funcionalidade, pois está além das necessidades do nosso aplicativo de demonstração.
2. Pyramid
A ferramenta de bootstrapping de Pyramid é chamada pcreate. Anteriormente, usava-se o conjunto de ferramentas Paste, mas foi substituído por um conjunto de ferramentas específico para o Pyramid.
1 |
[crayon-6743272869fde946940204 inline="true" class=" language-bash" data-language="bash"]$ pcreate <span class="token operator">-</span>s starter hello_pyramid <span class="token comment" spellcheck="true"># Just make a Pyramid project</span> |
Pyramid destina-se a aplicações maiores e mais complexas do que o Flask. Devido a isso, sua ferramenta de bootstrapping cria um projeto de esqueleto maior. Ele também lança arquivos de configuração básicos, um template de exemplo e os arquivos para empacotar seu aplicativo para fazer o upload para o Índice de Pacotes do Python.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
[crayon-6743272869fe0951155124 inline="true" class=" language-markup" data-language="markup"]hello_pyramid ├── CHANGES.txt ├── development.ini ├── MANIFEST.in ├── production.ini ├── hello_pyramid │ ├── __init__.py │ ├── static │ │ ├── pyramid-16x16.png │ │ ├── pyramid.png │ │ ├── theme.css │ │ └── theme.min.css │ ├── templates │ │ └── mytemplate.pt │ ├── tests.py │ └── views.py ├── README.txt └── setup.py |
Como no resto do framework, o bootstrapper da Pyramid é incrivelmente flexível e não se limita a um aplicativo padrão. pcreate pode usar qualquer número de modelos de projeto. Incluído no pcreate há o modelo “starter” que usamos acima, juntamente com SQLAlchemy. No PyPi, é possível encontrar scaffolds prontos para o Google App Engine, o jQuery Mobile, o modelo de Jinja2, frameworks de frontend modernos e muitos mais.
3. Django
O Django também tem a sua própria ferramenta bootstrap incorporada como parte do django-admin.
1 2 |
django-admin startproject hello_django django-admin startapp howdy # make an application within our project |
Já podemos ver uma das maneiras pelas quais o Django difere do Pyramid. O Django separa um projeto em aplicativos individuais, enquanto Pyramid e Flask esperam que um projeto seja um único “aplicativo” com várias visualizações ou modelos. É possível replicar a distinção de projeto/app em Flask e Pyramid, mas a noção não existe por padrão.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[crayon-6743272869fe5567226618 inline="true" class=" language-markup" data-language="markup"]hello_django ├── hello_django │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── howdy │ ├── admin.py │ ├── __init__.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py └── manage.py |
Por padrão, o Django só inclui arquivos de modelos e modelos vazios, então um novo usuário vê um código de exemplo um pouco menor para começar. Também (infelizmente) deixa a escolha de como distribuir sua aplicação para o desenvolvedor.
A desvantagem da ferramenta bootstrapping não orientar os usuários a empacotar seus aplicativos é que os usuários novatos não vão fazê-lo. Se um desenvolvedor não tiver empacotado um aplicativo antes, eles serão surpreendidos na primeira implantação. Projetos com uma comunidade grande como django-oscar são empacotados e disponíveis em PyPi, mas projetos menores muitas vezes não tem um empacotamento apropriado.
Frameworks em ação
Para cada framework, vamos fazer um aplicativo chamado wut4lunch, uma rede social para dizer toda a internet o que você comeu para o almoço. Está aí uma ideia totalmente gratuita para uma startup :). O aplicativo será uma interface simples que permite aos usuários postar o que eles almoçaram e ver uma lista do que outros usuários comeram. A página inicial ficará assim quando terminarmos.
1. Flask
A execução mais curta em 34 linhas de Python e um único modelo de 22 linhas Jinja. Primeiro, temos algumas tarefas domésticas para fazer, como inicializar o nosso aplicativo e puxar o nosso ORM.
1 2 3 4 5 6 7 8 9 10 11 |
from flask import Flask # For this example we'll use SQLAlchemy, a popular ORM that supports a # variety of backends including SQLite, MySQL, and PostgreSQL from flask.ext.sqlalchemy import SQLAlchemy app = Flask(__name__) # We'll just use SQLite here so we don't need an external database app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db' db = SQLAlchemy(app) |
Agora, vamos dar uma olhada em nosso modelo, que permanecerá praticamente o mesmo para os outros dois exemplos.
1 2 3 4 5 |
class Lunch(db.Model): """A single lunch""" id = db.Column(db.Integer, primary_key=True) submitter = db.Column(db.String(63)) food = db.Column(db.String(255)) |
Bem fácil, não? A parte mais difícil foi encontrar os tipos de dados SQLAlchemy corretos e escolher um comprimento para os nossos campos String no banco de dados.
Construir nosso formulário de submissão também é fácil. Depois de importar Flask-WTForms e os tipos de campos corretos, você pode ver o que o formulário parece um pouco como o nosso modelo. A principal diferença é o novo botão “Submit”.
O campo SECRET_KEY na configuração do aplicativo é usado pelo WTForms para criar tokens CSRF. Ele também é usado por itsdangerous (incluído no Flask) para assinar cookies e outros dados.
1 2 3 4 5 6 7 8 9 10 |
from flask.ext.wtf import Form from wtforms.fields import StringField, SubmitField app.config['SECRET_KEY'] = 'please, tell nobody' class LunchForm(Form): submitter = StringField(u'Hi, my name is') food = StringField(u'and I ate') # submit button will read "share my lunch!" submit = SubmitField(u'share my lunch!') |
Vamos fazer o formulário aparecer no navegador:
1 2 3 4 5 6 7 |
from flask import render_template @app.route("/") def root(): lunches = Lunch.query.all() form = LunchForm() return render_template('index.html', form=form, lunches=lunches) |
Certo, o que aconteceu? Temos uma lista de todos os almoços que já foram postados com Lunch.query.all (), e instanciado um formulário para deixar o usuário postar sua própria aventura gastronômica. Para simplificar, as variáveis são passadas para o modelo com o mesmo nome, mas isso não é necessário.
1 2 3 4 5 6 |
<html> <title>Wut 4 Lunch</title> <b>What are people eating?</b> <p>Wut4Lunch is the latest social network where you can tell all your friends about your noontime repast!</p> |
Aqui está a verdadeira carne do modelo, onde passamos por todos os almoços que foram comidos e exibimos em um <ul>. Isso é quase idêntico ao exemplo de loop que vimos anteriormente.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<ul> {% for lunch in lunches %} <li><strong>{{ lunch.submitter|safe }}</strong> just ate <strong>{{ lunch.food|safe }}</strong> {% else %} <li><em>Nobody has eaten lunch, you must all be starving!</em></li> {% endfor %} </ul> <b>What are YOU eating?</b> <form method="POST" action="/new"> {{ form.hidden_tag() }} {{ form.submitter.label }} {{ form.submitter(size=40) }} <br/> {{ form.food.label }} {{ form.food(size=50) }} <br/> {{ form.submit }} </form> </html> |
A seção <form> do modelo apenas processa os rótulos de formulário e as entradas do objeto WTForm que passamos para o modelo na visualização root (). Quando o formulário é enviado, ele enviará uma solicitação POST para o endpoint /new, que será processado pela função abaixo.
1 2 3 4 5 6 7 8 9 10 11 |
from flask import url_for, redirect @app.route(u'/new', methods=[u'POST']) def newlunch(): form = LunchForm() if form.validate_on_submit(): lunch = Lunch() form.populate_obj(lunch) db.session.add(lunch) db.session.commit() return redirect(url_for('root')) |
Depois de validar os dados do formulário, colocamos o conteúdo em um dos nossos objetos Model e o enviamos para o banco de dados. Uma vez que armazenamos o almoço no banco de dados, ele aparecerá na lista de almoços que as pessoas comeram.
1 2 3 |
if __name__ == "__main__": db.create_all() # make our sqlalchemy tables app.run() |
Finalmente, temos que fazer um (pequeno) trabalho para realmente executar o nosso aplicativo. Usando SQLAlchemy, criamos a tabela que usamos para armazenar almoços e, em seguida, começamos a executar os manipuladores de rota que escrevemos.
2. Django
A versão do Django do wut4lunch é semelhante à versão do Flask, mas está espalhada por vários arquivos no projeto do Django. Primeiro, vamos olhar para a parte mais semelhante: o modelo de banco de dados. A única diferença entre esta e a versão SQLAlchemy é a sintaxe ligeiramente diferente para declarar um campo de banco de dados que contém texto.
1 2 3 4 5 6 |
# from wut4lunch/models.py from django.db import models class Lunch(models.Model): submitter = models.CharField(max_length=63) food = models.CharField(max_length=255) |
Sobre o sistema de formulários, ao contrário do Flask, o Django tem um sistema de formulários incorporado que podemos usar. Parece muito com o módulo WTForms que usamos no Flask, só que com sintaxe diferente.
1 2 3 4 5 6 7 8 9 10 11 12 |
from django import forms from django.http import HttpResponse from django.shortcuts import render, redirect from .models import Lunch # Create your views here. class LunchForm(forms.Form): """Form object. Looks a lot like the WTForms Flask example""" submitter = forms.CharField(label='Your name') food = forms.CharField(label='What did you eat?') |
Agora só precisamos fazer uma instância do LunchForm para passar para o nosso modelo.
1 2 3 4 5 6 7 8 9 10 11 12 |
lunch_form = LunchForm(auto_id=False) def index(request): lunches = Lunch.objects.all() return render( request, 'wut4lunch/index.html', { 'lunches': lunches, 'form': lunch_form, } ) |
A função render é um atalho do Django que leva a solicitação, o caminho do modelo e um dict de contexto. Semelhante ao render_template do Flask, mas também leva a solicitação de entrada.
1 2 3 4 5 6 |
def newlunch(request): l = Lunch() l.submitter = request.POST['submitter'] l.food = request.POST['food'] l.save() return redirect('home') |
Salvar a resposta do formulário para o banco de dados é diferente. Em vez de usar uma sessão de banco de dados global, o Django nos permite chamar o método .save () do modelo e manipular o gerenciamento de sessões de forma transparente.
O Django fornece alguns recursos agradáveis para gerenciar os almoços que os usuários enviaram, para que possamos excluir almoços que não são apropriados para nosso site. Flask e Pyramid não fornecem isso automaticamente. Tudo o que tínhamos de fazer para dizer ao Django-admin sobre os nossos modelos é adicionar duas linhas ao wut4lunch/admin.py.
1 2 |
from wut4lunch.models import Lunch admin.site.register(Lunch) |
E agora podemos adicionar e excluir entradas sem fazer nenhum trabalho extra. Por fim, vamos dar uma olhada nas diferenças no modelo da página inicial.
1 2 3 4 5 6 7 |
<ul> {% for lunch in lunches %} <li><strong>{{ lunch.submitter }}</strong> just ate <strong>{{ lunch.food }}</strong></li> {% empty %} <em>Nobody has eaten lunch, you must all be starving!</em> {% endfor %} </ul> |
Django tem um atalho acessível para referenciar outras vistas em suas páginas. A tag url permite que você reestruture os URLs que seu aplicativo atende sem quebrar suas visualizações.
1 2 3 4 5 |
<form action="{% url 'newlunch' %}" method="post"> {% csrf_token %} {{ form.as_ul }} <input type="submit" value="I ate this!" /> </form> |
O formulário é processado com sintaxe diferente, e precisamos incluir um token CSRF manualmente no corpo do formulário, mas essas diferenças são principalmente cosméticas.
3. Pyramid
Finalmente, vamos dar uma olhada no mesmo programa em Pyramid. A maior diferença de Django e Flask aqui é o modelo. Alterar o modelo Jinja2 foi o suficiente para resolver o nosso problema no Django. Desta vez, é diferente. A sintaxe do modelo Chameleon da Pyramid é mais uma reminiscência do XSLT do que qualquer outra coisa.
1 2 3 4 5 6 7 8 9 10 11 |
<!-- pyramid_wut4lunch/templates/index.pt --> <div tal:condition="lunches"> <ul> <div tal:repeat="lunch lunches" tal:omit-tag=""> <li tal:content="string:${lunch.submitter} just ate ${lunch.food}"/> </div> </ul> </div> <div tal:condition="not:lunches"> <em>Nobody has eaten lunch, you must all be starving!</em> </div> |
Como nos modelos do Django, a falta da construção for-else-endfor torna a lógica um pouco mais detalhada. Nesse caso, acabamos com blocos if-for e if-not-for para fornecer a mesma funcionalidade. Modelos que usam tags XHTML podem parecer estranhos depois de usar modelos de estilo Django e AngularJS que usam {{ or {% para estruturas de controle e condicionais.
Uma das grandes vantagens do estilo do Chameleon é que o editor de escolha irá destacar a sintaxe corretamente, uma vez que os modelos são XHTML válidos. Para os modelos Django e Flask, seu editor precisa ter suporte para linguagens de template para destacar corretamente.
1 2 3 4 5 6 7 8 9 10 |
<b>What are YOU eating?</b> <form method="POST" action="/newlunch"> Name: ${form.text("submitter", size=40)} <br/> What did you eat? ${form.text("food", size=40)} <br/> <input type="submit" value="I ate this!" /> </form> </html> |
A renderização do formulário é ligeiramente mais detalhada no Pyramid porque o pyramid_simpleform não tem um equivalente ao form.as_ul do Django, que processa todos os campos do formulário automaticamente.
Agora vamos ver o que sustenta a aplicação. Primeiro, vamos definir o formulário que precisamos e processar a nossa homepage.
1 2 3 4 5 6 7 8 9 10 11 |
# pyramid_wut4lunch/views.py class LunchSchema(Schema): submitter = validators.UnicodeString() food = validators.UnicodeString() @view_config(route_name='home', renderer='templates/index.pt') def home(request): lunches = DBSession.query(Lunch).all() form = Form(request, schema=LunchSchema()) return {'lunches': lunches, 'form': FormRenderer(form)} |
A sintaxe de consulta para recuperar todos os almoços é parecida com a do Flask porque ambos os aplicativos de demonstração usam o popular SQLAlchemy ORM para fornecer armazenamento persistente. No Pyramid, você pode retornar o dicionário de contexto do seu modelo diretamente, em vez de precisar chamar uma função de render especial. O decorador @view_config automaticamente passa o contexto retornado para o modelo a ser processado. Ser capaz de pular a chamada do método de renderização faz com que as funções escritas no Pyramid sejam mais fáceis de testar, uma vez que os dados que retornam não são obscurecidos em um objeto de renderização de modelo.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@view_config(route_name='newlunch', renderer='templates/index.pt', request_method='POST') def newlunch(request): l = Lunch( submitter=request.POST.get('submitter', 'nobody'), food=request.POST.get('food', 'nothing'), ) with transaction.manager: DBSession.add(l) raise exc.HTTPSeeOther('/') |
Os dados do formulário são fáceis de recuperar do objeto de solicitação da Pyramid, que analisou automaticamente os dados POST do formulário em um dict que nós podemos acessar. Para evitar que múltiplas solicitações simultâneas acessem o banco de dados ao mesmo tempo, o módulo ZopeTransactions fornece gerenciadores de contexto para agrupar as gravações do banco de dados em transações lógicas e impedir que os threads do seu aplicativo pisoteiem as alterações de cada um, o que pode ser um problema se seu aplicativo receber muito tráfego.
Conclusão
Pyramid é o mais flexível das três. Ele pode ser usado para pequenos aplicativos como nós vimos aqui, mas também é usado por grandes sites como o Dropbox. Comunidades de código aberto como o Fedora o escolhem para aplicativos como o sistema de crachás da comunidade, que recebe informações sobre os eventos de muitas das ferramentas do projeto para atribuir distintivos de estilo de realização aos usuários. Uma das queixas mais comuns sobre o Pyramid é que apresenta tantas opções que pode ser intimidante para iniciar um novo projeto.
De longe, o framework mais popular é o Django, e a lista de sites que o usam é impressionante. Bitbucket, Pinterest, Instagram e The Onion usam o Django. Para sites que têm requisitos comuns, o Django escolhe padrões muito saudáveis e, por isso, tornou-se uma escolha popular para aplicativos de médio a grande porte na web.
Flask é ótimo para desenvolvedores que trabalham em pequenos projetos que precisam de uma maneira rápida de fazer um simples site em Python. Projetos de backend que precisam de uma interface web simples e rápida de desenvolver, exigindo pouca configuração geralmente se beneficiam do Flask no frontend, como o jitviewer, que fornece uma interface web para inspecionar logs do compilador just-in-time PyPy.