Desenvolvendo uma pequena aplicação web com Python e Flask
Neste artigo será explicado como configurar o seu ambiente de desenvolvimento, desenvolver uma aplicação web em python modularizada e no final publicá-la em um servidor free.
Tópicos abordados:
- Virtualenv – Ambientes Virtuais Independentes
- Flask – Micro Framework de desenvolvimento web para Python
- Python Any Where – Publicando de maneira simples e rápida
O que é o Virtualenv?🔗
Virtualenv é um software que permite a criação de ambientes virtuais com total independência dos outros ambientes criados no Virtualenv. Isso permite que cada ambiente tenha autonomia para instalar plug-ins e bibliotecas de forma que a configuração de um ambiente não impacte nos restantes. Um exemplo prático seria a possibilidade de ter várias versões do Python, diferente para cada projeto, instalado na mesma máquina.
🔗
Instalando o Virtualenv🔗
Para facilitar nossa vida vamos utilizar um módulo Python chamado easy_install que gerencia pacotes Python e possui ferramentas de instalação que realiza automaticamente download, build e instalação dos pacotes disponíveis (no quais são muitos).
sudo easy_install virtualenv
Caso você não tenha o easy_install configurado em sua máquina ainda, execute o seguinte comando no Terminal:
sudo apt-get install python-setuptools python-dev build-essential
A mensagem de sucesso deverá ser algo parecida com a seguinte:
Searching for virtualenv
Best match: virtualenv 1.8.4
Adding virtualenv 1.8.4 to easy-install.pth file
Installing virtualenv script to /usr/local/bin
Installing virtualenv-2.7 script to /usr/local/bin
Using /usr/local/lib/python2.7/dist-packages
Processing dependencies for virtualenv
Finished processing dependencies for virtualenv
Criando o Primeiro Ambiente Virtual🔗
Antes de criar o primeiro ambiente virtual vamos separar um diretório para centralizar todos os documentos, códigos-fontes, configurações e outros assuntos relacionados a Desenvolvimento em um diretório chamado Development na Home do seu usuário no Linux:
cd ~/
mkdir Development
Para manter os ambientes virtuais bem organizados, vamos criar um diretório chamado virtualenvs dentro de Development:
cd ~/Development
mkdir virtualenvs
Agora sim vamos criar nosso primeiro ambiente virtual dentro do diretório virtualenvs. Como mais pra frente nesse post irei explicar como desenvolver uma pequena aplicação, esse ambiente virtual será focado para esse projeto. Então meu ambiente virtual se chamará i-sweated-yesterday:
cd virtualenvs
virtualenv i-sweated-yesterday
A mensagem de sucesso deverá ser algo parecida com a seguinte:
New python executable in i-sweated-yesterday/bin/python
Installing setuptools............done.
Installing pip...............done.
Caso você desejasse criar um ambiente que não utilize qualquer biblioteca pré instalada no sistema operacional, deveria ser executado da forma a baixo:
virtualenv i-sweated-yesterday --no-site-packages
🔗
Utilizando o Ambiente Virtual Criado🔗
Ativando🔗
Para utilizar o ambiente virtual você deve ativá-lo antes de realizar qualquer configuração, como instalar ou utilizar alguma biblioteca instalada no mesmo. O comando para ativar o ambiente virtual criado anteriormente é o seguinte:
source i-sweated-yesterday/bin/activate
Sempre que o ambiente virtual estiver ativado irá exibir o nome do ambiente virtual no lado esquerdo das linhas de comando no Terminal (Prompt). No meu caso fica da seguinte forma:
(i-sweated-yesterday)max@Max-Xub-VM:~$
A partir de agora qualquer configuração realizada estará associada apenas ao ambiente virtual ativo no momento.
Desativando🔗
Quando não existir mais a necessidade de utilizar o ambiente virtual no momento, você pode voltar para o ambiente do sistema operacional desativando o ambiente virtual atual:
(i-sweated-yesterday)max@Max-Xub-VM:~$ deactivate
🔗
Visualizar os Pacotes Instalados no Ambiente Virtual Atual🔗
Com o ambiente virtual ativado vamos instalar o pacote chamado yolk, que possui a funcionalidade de listar os pacotes já instalados no ambiente atual:
sudo easy_install yolk
Após instalado execute o seguinte comando para visualizar os pacotes instalados:
yolk -l
O que é o Flask?🔗
Flask é um micro framework de desenvolvimento web para Python baseado em duas bibliotecas externas: O Jinja2 e o Werkzeug. Por mais que seja considerado “micro”, Flask suporta extensões de diversas funcionalidades para a sua aplicação, como integração de ORM, validação de formulários, open autenticações e outras diversas extensões.
🔗
Configurações🔗
Instalando o Flask🔗
Para utilizar o Flask precisamos instalá-lo primeiro (não esqueça de ativar o ambiente virtual antes):
pip install Flask
Vamos aproveitar que estamos instalando o Flask e já vamos instalar alguns outros pacotes para estender as funcionalidades do Flask e deixar o ambiente configurado para nosso aplicativo.
Instalando SQLAlchemy🔗
SQLAlchemy é uma poderosa ferramenta ORM que possibilita aos desenvolvedores trabalhar com dados armazenados no banco de dados de maneira flexível e simplificada.
pip install SQLAlchemy
🔗
Instalando o Flask-SQLAlchemy🔗
Flask-SQLAlchemy adiciona funcionalidades ao Flask que facilitam algumas tarefas comuns ao utilizar o SQLAlchemy.
pip install flask-sqlalchemy
🔗
Instalando o Flask-WTF🔗
Flask-WTF integra de maneira simples o WTForms ao Flask, fornecendo uma maneira fácil de lidar com a apresentação de dados do usuário.
pip install Flask-WTF
Instalando o SQLite3🔗
SQLite é um pacote que disponibiliza um Sistema Gerenciador de Banco de Dados Relacional e permite ser executado através de linha de comando, possibilitando executar qualquer query SQL básica de maneira simples. (Nossa aplicação não dependerá desse pacote para ser executada, mas seria bom já instalá-lo caso surja a necessidade de executar alguma query SQL no banco)
sudo apt-get install sqlite3 libsqlite3-dev
Criando uma Aplicação Flask para Teste🔗
Antes de continuar com a nossa aplicação vamos criar uma aplicação em Flask o mais simples possível, apenas para verificar se o Flask foi instalado corretamente e está funcionando da maneira esperada.
Crie um arquivo chamado hello.py em qualquer diretório dentro de ~/Development:
Os parâmetros debug e port são opcionais mas facilita muito que o debug esteja ativado para visualizar erros que ocorram durante a codificação e utilizando uma porta diferente do padrão 80 possibilita que você rode mais de uma aplicação ao mesmo tempo sem ocorrer o conflito de portas.
Definição do __name__ :
- Se o módulo é executado diretamente o __name__ é __main__
- Se o módulo é importado por outro módulo então __name__ é o nome do próprio módulo
Com o virtualenv ativado, rode a linha de comando a baixo e em seguida abra o navegador no endereço “http://localhost:8085 ”:
python hello.py
Caso esteja tudo correto então você deverá visualizar uma página com a mensagem “Hello World”.
Desenvolvendo a Aplicação “I Sweated Yesterday”🔗
I Sweated Yesterday🔗
É uma aplicação pequena e simples para controlar a frequência de dias que um usuário realiza exercícios físicos. É chamado “Yesterday” porque na empresa em que trabalho, sempre marcamos o exercício físico ao chegarmos na empresa no dia seguinte. Foi baseada na aplicação exemplificada por Armin Ronacher nesta página no Github.
O código fonte da versão dessa aplicação exemplificada nesse post está disponível para download aqui e a versão mais atual está aqui, qual ainda darei mais alguns retoques.
Visão Geral da Estrutura do Projeto🔗
Minha dica aqui é que você crie os arquivos e diretórios abaixo para facilitar quando formos codificar ou faça o download do código fonte a cima.
Diretório Base🔗
Possui arquivos comuns para toda aplicação
/i-sweated-yesterday | |
---|---|
/i-sweated-yesterday/initialize-db.py | Configurações para inicializar o banco de dados |
/i-sweated-yesterday/config.py | Variáveis globais da aplicação |
/i-sweated-yesterday/app.db | Arquivo de banco de dados |
/i-sweated-yesterday/run.py | Roda a aplica cação |
Diretório da Aplicação🔗
Possui o arquivo main, responsável por toda aplicação
/i-sweated-yesterday/app | |
---|---|
/i-sweated-yesterday/app/__init__.py | Faz com que o Python trate o diretório como um módulo, realiza a inicialização do Flask, SQLAlchemy e realiza a configuração de rotas básicas |
Diretório – Módulo Users🔗
Possui arquivos relacionados ao módulo Users, como formulários, classes modelos (entidade/mapeamento banco), páginas htmls…
/i-sweated-yesterday/app/users | |
---|---|
/i-sweated-yesterday/app/users/__init__.py | Faz com que o Python trate o diretório como um módulo |
/i-sweated-yesterday/app/users/views.py | Onde será tratado as requisições (Similar a Controller no MVC) |
/i-sweated-yesterday/app/users/forms.py | Definição formulários |
/i-sweated-yesterday/app/users/constants.py | Definição de variáveis Constantes |
/i-sweated-yesterday/app/users/models.py | Definição de Modelos |
/i-sweated-yesterday/app/users/decorators.py | Definição de Decorators |
/i-sweated-yesterday/app/users/requests.py | Definição de Requisições Comuns |
Diretório – Módulo Exercises🔗
Possui arquivos relacionados ao módulo Exercises, como formulários, classes modelos (entidade/mapeamento banco), páginas htmls…
/i-sweated-yesterday/app/exercises | |
---|---|
/i-sweated-yesterday/app/exercises/__init__.py | Faz com que o Python trate o diretório como um módulo |
/i-sweated-yesterday/app/exercises/helpers.py | Definição de algumas funções uteis |
/i-sweated-yesterday/app/exercises/views.py | Onde será tratado as requisições (Similar a Controller no MVC) |
/i-sweated-yesterday/app/exercises/forms.py | Definição formulários |
/i-sweated-yesterday/app/exercises/constants.py | Definição de variáveis Constantes |
/i-sweated-yesterday/app/exercises/models.py | Definição de Modelos |
Diretório Static🔗
Possui arquivos estáticos como css, imagens e javascripts
/i-sweated-yesterday/app/static |
---|
/i-sweated-yesterday/app/static/css |
/i-sweated-yesterday/app/static/css/reset.css |
/i-sweated-yesterday/app/static/css/main.css |
/i-sweated-yesterday/app/static/js |
/i-sweated-yesterday/app/static/js/main.js |
/i-sweated-yesterday/app/static/img |
Diretório Templates🔗
Possui arquivos de templates, ou seja, todas as páginas htmls puras ou com definições Jinja2
/i-sweated-yesterday/app/templates |
---|
/i-sweated-yesterday/app/templates/base.html |
/i-sweated-yesterday/app/templates/users |
/i-sweated-yesterday/app/templates/users/register.html |
/i-sweated-yesterday/app/templates/users/profile.html |
/i-sweated-yesterday/app/templates/users/login.html |
/i-sweated-yesterday/app/templates/forms |
/i-sweated-yesterday/app/templates/forms/macros.html |
/i-sweated-yesterday/app/templates/404.html |
/i-sweated-yesterday/app/templates/exercises |
/i-sweated-yesterday/app/templates/exercises/i_did.html |
Configuração🔗
O arquivo run.py será utilizando durante o desenvolvimento para inicializar a aplicação e disponibilizar o acesso pelo browser no endereço http://localhost:8090:
O arquivo config.py será utilizado para definição de valores comuns de toda aplicação. Mantendo essas informações centralizadas nesse arquivo facilitará durante a publicação para o ambiente de produção ou de testes, que essas informações sejam alteradas de acordo com a necessita de cada ambiente:
=
= False
=
=
# Define the path of our database inside the root application,
# where 'app.db' is the database's name
= +
=
= 8
= True
=
_basedir : mantém o caminho de onde os scripts são executados
DEBUG : utilizado como True em ambientes de desenvolvimento, permite que seja exibido detalhadamente a requisição, caso ocorra algum erro durante o processo
SECRET_KEY : utilizado na criação dos cookies
ADMINS : email do Administrador responsável pelo site
SQLALCHEMY_DATABASE_URI e DATABASE_CONNECT_OPTIONS : opções de conexão do SQLAlchemy
CSRF_ENABLED e CSRF_SESSION_KEY : proteção contra fraude de posts
O arquivo initialize-db.py será utilizado para criar a estrutura de tabelas que iremos configurar mais adiante em nossas Models:
# Drop all tables from db file
# Create all tables on db file,
# copying the structure from the definition on the Models
Main🔗
O arquivo __init__.py será o main da aplicação, nele estará a definição do Flask, SQLAlchemy e as rotas comuns:
=
=
# Basic Routes #
return
return , 404
# BluePrints - Modules #
# Users Module
# Exercises Module
Blueprint basicamente permite que um módulo estenda a aplicação principal e funcione similarmente a aplicação Flask. Sendo esta uma das grandes vantagem para aplicações maiores, por permitir a modularização de uma aplicação, o que facilita em muito a organização, desenvolvimento e manutenções do código fonte.
Veja mais sobre Blueprints.
Módulo Usuário🔗
Como este será um módulo estendido da aplicação principal, o nosso arquivo __init__.py estará vazio, pois objetos que precisamos já foram inicializados pela Main.
O arquivo constants.py é simples e contêm apenas valores Constantes, centralizados todos no mesmo arquivo para priorizar a organização e facilitar caso surja a necessidade de alguma manutenção futura:
# User role
= 0
= 1
= 2
=
# User status
= 0
= 1
= 2
=
# Session Name
=
O models.py contém a definição da model User, sendo basicamente estruturada pela definições de propriedades, mapeamentos model/database, construtores da classe e métodos GET e SET:
# third party imports
# local application imports
# Map model to db table
=
=
=
=
=
=
=
# Class Constructor
=
# Factory Constructor of a new user to register
=
=
=
=
return
return
return
return
return
=
=
=
return
=
return
=
return
return %
O forms.py utiliza o módulo WTForms para gerar formulários de maneira fácil e simples, como campos associados a labels e propriedades Required, caso seja obrigatório:
from flask.ext.wtf import Form, TextField, PasswordField, BooleanField
from flask.ext.wtf import Required, Email, EqualTo
class LoginForm(Form):
email = TextField('Email address', [Required(), Email()])
password = PasswordField('Password', [Required()])
class RegisterForm(Form):
name = TextField('NickName', [Required()])
email = TextField('Email address', [Required()])
password = PasswordField('Password', [Required()])
confirm = PasswordField('Repeat Password', [
Required(),
EqualTo('confirm', message='Passwords must match')
])
O decorators.py possui a declaração para o decorator requires_login, no qual verifica se o usuário está logado, caso não esteja redireciona o usuário para a página de login:
return
return
return
flask.g : é um objeto no Flask que possui a responsabilidade de armazenar e compartilhar dados através do tempo de execução de uma requisição
*args : o * é utilizado para permitir que um parâmetro aceite um número não definido de argumentos para sua função (parecido com a keyword params no C#)
**kwargs : Da mesma forma, ** permite lidar com argumentos nomeados que não foram previamente definidos (parecido com a keyword params+dictonary no C#)
O requests.py possui a declaração de uma função comum a várias páginas na aplicação. No qual se existir um usuário logado na requisição atual será recuperado o Id do usuário na sessão, então recupera-rá o objeto usuário no banco e será mantido até o final da requisição no objeto flask.g.user:
# third party imports
# local application imports
# pull user's profile from the db before every request are treated
= None
=
O views.py, para quem está acostumado com MVC funciona como uma Controller, é onde será definido as requisições possíveis para esse módulo, associando as rotas existentes as funcionalidades específicas de cada uma. Como dito anteriormente esse módulo não será uma aplicação e sim um módulo estendido da aplicação principal. Então ao invés de utilizarmos um objeto do tipo Flask, como para a definição de rotas, iremos utilizar o Blueprint.
# third party imports
# local application imports
=
return
=
# make sure data are valid, but doesn't validate password is right
=
# we use werzeug to validate user's password
# the session can't be modified as it's signed,
# it's a safe place to store the user id
=
return
return
=
=
return
# create an user instance not yet stored in the database
=
# insert the record in our database and commit it
# log the user in, as he now has an id
=
# flash will display a message to the user
# redirect user to the 'index' method of the user module
return
return
# remove the username from the session if it's there
# flash will display a message to the user
# redirect user to the 'index' method of the user module
return
validate_on_submit : função do WTForm, no qual verifica se o formulário é valido durante uma requisição do tipo POST ou PUT
render_template : retorna um documento renderizado, no nosso caso todos html, de acordo com o arquivo template e o formulário passado
flash : possibilita que mensagens declaradas na view sejam recuperadas no template e exibidas para o usuário
@mod.route('/login/', methods=['GET', 'POST']) : associa uma rota a uma função que estiver após sua definição. O primeiro parâmetro define por qual caminho será executado e o segundo parâmetro quais os métodos REST serão aceitos por essa rota.
Templates🔗
Como Flask já possui integrado o Jinja, vamos utilizar em nossos templates algumas de suas funcionalidades disponíveis, como estruturas de condições, laços de repetições, blocos de conteúdos. Permitindo um alto nível de reutilização e fácil desenvolvimento.
O base.html será o template base, do qual será herdado pelos outros templates. Nesse template teremos a definição do corpo HTML, HEAD, BODY e blocos de conteúdo que poderão ser substituídos com outros conteúdos pelos templates que o herdarem.
{% block title %}I Sweated Yesterday{% endblock %}
{% block css %}
{% endblock %} {% block script %}
{% endblock %}
{% block header %}
I Sweated Yesterday
{% endblock %}
{% for category, msg in get_flashed_messages(with_categories=true) %} {{ msg }} {% endfor %}
{% block logout %}
Logout
{% endblock %} {% block content %}{% endblock %}
{% block footer %}{% endblock %}
O macros.html possuirá definições de macros. No nosso caso criamos um macro chamado render_field no qual será responsável por criar uma estrutura HTML para os campos dos nossos formulários. Um detalhe importante desse macro é que caso exista alguma validação que não tenha passado durante a execução do validate_on_submit na view, então as mensagens de erro serão adicionados no HTML gerado.
{% macro render_field(field) %}
{{ field.label(class="label") }} {% if field.errors %} {% set css_class = 'has_error' + kwargs.pop('class', '') %} {{
field(class=css_class, **kwargs) }}
{% for error in field.errors %}
{{ error|e }}
{% endfor %}
{% else %} {{ field(**kwargs) }} {% endif %}
{% endmacro %}
O register.html herda o base.html e substitui os blocos de conteúdo logout, content e footer. Este template será responsável pelo HTML utilizado no registro de um novo usuário, por isso o método utilizado é o POST e action referencia a url atual sem o nome da página, no qual se verificarmos na view estará apontando para a função register:
{% extends "base.html" %} {% block logout %}<!-- remove btn logout -->{% endblock %} {% block content %} {% from
"forms/macros.html" import render_field %}
{{ form.csrf_token }} {{ render_field(form.name, class="input text") }} {{ render_field(form.email, class="input
text") }} {{ render_field(form.password, class="input text") }} {{ render_field(form.confirm, class="input text") }}
{% endblock %} {% block footer %}
Login
{% endblock %}
A partir de agora será melhor que você siga o resto da aplicação analisando e copiando direto do código fonte disponibilizado mais a cima nesse tutorial, se não este artigo irá ficar muito maior do que já está.
Executando a Aplicação🔗
No primeiro momento que executarmos a aplicação é necessário inicializar o banco de dados, para isso abra o Terminal do sistema operacional, acesse o diretório da aplicação e execute a linha de comando a baixo (não esqueça de antes ativar o ambiente virtual):
python initialize-db.py
Após executado a linha de comando a cima, só a execute novamente caso você deseje resetar o banco de dados, pois será criado as tabelas novamente e perderá todos os dados existentes.
Para inicializar a aplicação execute a linha de comando a baixo:
python run.py
Se a aplicação não possuir nenhuma falha grave será exibido a seguinte mensagem no Terminal:
* Running on http://127.0.0.1:8090/
* Restarting with reloader
Para visualizar a aplicação abra qualquer browser com a seguinte URL: http://127.0.0.1:8090/.
O que é o Python Any Where?🔗
O Python Any Where é uma IDE online e serviço de hospedagem baseado em Python. No qual disponibiliza a hospedagem web de aplicações python e acesso ao um console online possibilitando desenvolvimento em python com várias bibliotecas disponíveis de maneira online, fácil e gratuita para o plano mais simples.
Publicando🔗
-
Crie um cadastro no Python Any Where
-
Selecione a opção I want to create a web application
-
Selecione a aba Web e em seguida a opção Add a new web app
-
Selecione Next na primeira janela
-
Selecione Flask para definir o Framework Web Python a ser utilizado
-
Selecione Next para confirmar o caminho físico da sua aplicação
-
Após criada a opção It is configured via WSGI file stored at para editar arquivo wsgi
-
Edite o arquivo wsgi, atualizando o valor da variável projec*home com o caminho físico correto da sua aplicação (abra a aba Files para confirmar o caminho correto) e na última linha certifique-se de estar realizando a importação correta, caso você tenha deixado a variável instanciado do Flask no arquivo __init__.py da aplicação como “app”, então você deixará a última linha da forma que está:**/ > var > www > YOUR-USER-NAMEpythonanywhere_com_wsgi.py* :**
# This file contains the WSGI configuration required to serve up your # web application at http://<your-username>.pythonanywhere.com/ # It works by setting the variable 'application' to a WSGI handler of some # description. # # The below has been auto-generated for your Flask project # add your project directory to the sys.path = u = + # import flask app but need to call it "application" for WSGI to work #from flask_app import app as application #example
-
Salve o arquivo anterior
-
Acesse a aba Files e em seguida o diretório : / > home > YOUR-USER-NAME
-
Clique no botão Escolher Arquivo
-
Selecione o seu projeto compactado como .zip
-
Clique em Upload
-
Acesse a aba Consoles e selecione a opção Bash
-
Execute o seguinte comando no console:
unzip SEU-PROJETO-COMPACTADO.zip
-
Para inicializar o banco de dados execute o seguinte comando no console:
python YOUR-PROJECT-DIRECTORY/initialize-db.py
-
Acesse a aba Web e clique em Reload web app
-
Finalmente para visualizar o aplicativo rodando clique no link You can see your web app at disponível na aba Web (caso ocorra algum erro ao abrir a aplicação, veja a descrição mais detalhada do problema nos logs de erros disponíveis também na aba Web)
Wsgi (Web Server Gateway Interface): Define uma simples interface entre Servidores Web e Aplicações Web ou Frameworks para Python.
Dica : Utilizamos a função de Upload para enviar o código fonte da nossa aplicação do computador local para o servidor web. Mas seria ainda mais fácil quando utilizado o Dropbox ou melhor ainda o Github. De uma olhada nisso, vai facilitar ainda mais o deploy quando realizado várias vezes.