2015-10-20

What The Flask - 3/6

Finalmente!!! A terceira parte da série What The Flask, mas ainda não acabou, serão 6 artigos para se tornar um Flasker, neste capítulo falaremos sobre como instalar e configurar as principais extensões do Flask para torna-lo uma solução full-stack com bootstrap no front-end, ORM para banco de dados, admin parecido com o Django Admin, Cache, Sistema de filas (celery/huey), Controle de Acesso, Envio de email, API REST e Login Social.


Extending Flask

Hello Flask: Introdução ao desenvolvimento web com Flask

Flask patterns: Estruturando aplicações Flask

Plug & Use: extensões essenciais para iniciar seu projeto. <-- Você está aqui

DRY: Criando aplicativos reusáveis com Blueprints

from flask.ext import magic: Criando extensões para o Flask e para o Jinja2

Run Flask Run: "deploiando" seu app nos principais web servers e na nuvem.

Micro framework? Bom, o Flask foi criado com a premissa de ser um micro-framework, o que significa que ele não tem a intenção de entregar de bandeja para você todas as coisas que você precisa em único pacotinho e nem comandos mágicos que você roda e instantaneamente tem todo o seu projeto pronto. A idéia do Flask é ser pequeno e te dar o controle de tudo o que acontece no seu aplicativo, mas ao mesmo tempo o Flask se preocupa em ser facilmente extensivel, para isso os desenvolvedores pensaram em padrões que permitem que as extensões sejam instaladas de modo que não haja conflitos (lembra dos BluePrints do capítulo anterior?), além dos BluePrints tem também os patterns para desenvolvimento de extensions que ajuda a tornar a nossa vida mais fácil, nesta parte dessa série vamos instalar e configurar algumas das principais extensões do Flask (todas testadas por mim em projetos reais).

CMS de notícias

Nesta série estamos desenvolvendo um mini CMS para publicação de notícias, o código está disponível no github e para cada fase da evolução do projeto tem uma branch diferente. Esse aplicativo de notícias tem os seguintes requisitos:

Front-end usando o Bootstrap

Banco de dados MongoDB

Controle de acesso para que apenas editores autorizados publiquem notícias

Interface administrativa para as notícias e usuários

Cache das notícias para minimizar o acesso ao banco de dados

NOTE: Existem várias extensões para Flask, algumas são aprovadas pelos desenvolvedores e entram para a lista disponível no site oficial, algumas entram para a listagem do metaflask (projeto em desenvolvimento), e uma grande parte está apenas no github. Como existem várias extensões que fazem a mesma coisa, as vezes é dificil escolher qual delas utilizar, eu irei mostrar aqui apenas as que eu utilizo e que já tenho experiência, mas isso não quer dizer que sejam as melhores, sinta-se a vontade para tentar com outras e incluir sua sugestão nos comentários.

Flask Bootstrap - Para deixar as coisas bonitinhas

Flask MongoEngine - Para armazenar os dados em um banco que é fácil fácil!

Flask Security - Controle de acesso

Flask-Admin - Um admin tão poderoso quanto o Django Admin

Flask Cache - Para deixar o MongoDB "de boas"

Bônus: Utilizaremos a Flask-DebugToolbar

TL;DR: A versão final do app deste artigo esta no github, os apressados podem querer executar o app e explorar o seu código antes de ler o artigo completo.

Deixando as coisas bonitinhas com o Bootstrap!

Atualmente a versão do nosso CMS está funcional porém bem feinha, não trabalhamos no design das páginas pois obviamente este não é o nosso objetivo, mas mesmo assim podemos deixar as coisas mais bonitinhas.


Atual Layout do nosso CMS

Com a ajuda do Bootstrap e apenas uns ajustes básicos no front end podemos transformar o layout em algo muito mais apresentável.

Usaremos a extensão Flask-Bootstrap que traz alguns templates de base e utilidades para uso do Bootstrap no Flask.

Comece editando o arquivo de requirements adicionando Flask-Bootstrap

Arquivo requirements.txt

Agora instale as dependencias em sua virtualenv.

Agora com o Flask-Bootstrap instalado basta iniciarmos a extensão durante a criação de nosso app.

Editando o arquivo news_app.py incluiremos:

Sendo que o arquivo completo ficaria:

As extensões Flask seguem dois padrões de inicialização: Imediato e Lazy, é recomendado que toda extensão siga este protocolo.

Inicialização imediata:

Da forma acima sempre importamos uma classe com o nome da extensao e então passamos o nosso app como parametro na inicialiação da extensão. Assim durante o init da extensão ela poderá injetar templates, modificar rotas e adicionar configs no app que foi passado como parametro.

Inicialização Lazy:

Geralmente o primeiro modo inicio imediato é o mais utilizado, o carregamento Lazy é útil em situações mais complexas como por exemplo se o seu sistema estiver esperando a conexão com um banco de dados.

NOTE: Toda extensão do Flask deve começar com o nome flask_ para ser considerada uma extensão dentro dos padrões.

No nosso caso utilizamos Bootstrap(app) e agora o bootstrap já está disponível para ser utilizado em nossos templates!

Customizando os templates com o BootStrap.

Precisaremos efetuar algumas mudanças nos templates para que eles utilizem os estilos do Bootstrap 3.x

Não entrarei em detalhes a respeito da estensão Flask-Bootstrap pois temos mais uma série de extensões para instalar, mas você pode consultar a documentação oficial para saber mais a respeito dos blocos de template e utilidades disponíveis.

Comece alterando o template base.html para:

Algumas coisas continuaram iguais ao template antigo, porém agora estamos utilizando blocos navbar e content definidos pelo Flask-Bootstrap e criamos um novo bloco news que usaremos para mostras as nossas notícias.

Altere o template index.html

apenas mudamos o nome do bloco utilizado de content para news e adicionamos um título.

Altere o template noticia.html

Novamente mudamos o bloco principal de content para news

Altere os templates cadastro.html e cadastro_sucesso.html

Altere o bloco utilizado de content para news e o restante deixe como está por enquanto.

Resultado Final!

Antes do bootstrap



e depois:

O diff com as mudanças que foram feitas pode ser acessado neste link

Agora que o app já está com uma cara mais bonita, vamos passar para o próximo requisito: Banco de Dados.

Até agora usamos o dataset que é uma mão na roda! integrando facilmente o nosso projeto com bancos como MySQL, Postgres ou SQLite.

Porém nosso site de notícias precisa utilizar MongoDB e para isso vamos recorrer ao Flask-MongoEngine

Utilizando MongoDB com Flask

Vamos migrar do Dataset + SQlite para MongoDB e obviamente você irá precisar do MongoDb Server rodando, você pode preferir instalar o Mongo localmente se estiver em uma máquina Linux/Debian: sudo apt-get install mongodb ou siga instruções no site oficial do Mongo de acordo com seu sistema operacional.

Uma opção melhor é utilizar o docker para executar o MongoDB, desta forma você não precisa instalar o servidor de banco de dados em seu computador, precisa apenas do Docker e da imagem oficial do Mongo.

Vou mostrar os preocedimentos para instalação e execução no Linux/Debian, mas você pode tranquilamente utilizar outro S.O bastando seguir as instruções encontradas no site do docker.

Instalando o docker

No linux a maneira mais fácil de instalar o Docker é rodando o seguinte comando

Se você precisar de ajuda ou estiver usando um sistema operacional alternativo pode seguir as instruções do site oficial

Executando um container MongoDB

No DockerHub está disponível a imagem oficial do Mongo, basta executar o comando abaixo para ter o Mongo rodando em um container.

A parte do $PWD/mongodata: pode ser substituida pelo caminho de sua preferencia, este é o local onde o Mongo irá salvar os dados.

Se preferir executar no modo efemero (perdendo todos os dados ao reiniciar o container) execute apenas docker run -d -p 27017:27017 mongo

Instalando o MongoDB localmente (não recomendado, use o docker!)

Você pode preferir não usar o Docker e instalar o Mongo localmente, baixe o mongo descompacte, abra um console separado e execute: ./bin/mongod --dbpath /tmp/ lembrando de trocar o /tmp por um diretório onde queira salvar seus dados.

Se preferir utilize os pacotes oficiais do seu sistema operacional para instalar o Mongo.

IMPORTANTE: Para continuar você precisa ter uma instância do MongoDB rodando localmente, no Docker ou até mesmo em um servidor remoto se preferir.

Flask-Mongoengine

Adicione a extensão Flask-Mongoengine ao seu arquivo requirements.txt

Agora execute pip install -r requirements.txt --upgrade estando na virtualenv de seu projeto.

Substituindo o Dataset pelo MongoEngine

Agora vamos substituir o dataset pelo MongoEngine, por padrão o MongoEngine tentará conectar no localhost na porta 27017 e utilizar o banco de dados test. Mas no nosso caso é essencial informarmos exatamente as configurações desejadas.

No arquivo de configuração em development_instance/config.cfg adicione as seguintes linhas:

Agora vamos ao arquivo db.py vamos definir a conexão com o banco de dados Mongo, apague todo o conteúdo do arquivo e substitua por:

db.py

Crie um novo arquivo chamado models.py, é nesse arquivo que definiremos o esquema de dados nas nossas notícias. Note que o Mongo é um banco schemaless, poderiamos apenas criar um objeto Noticia(db.DynamicDocument) usando herança do DynamicDocument e isso tiraria a necessidade da definição do schema, porém, na maioria dos casos definir um schema básico ajuda a construir formulários e validar os dados.

models.py

Nosso próximo passo é alterar as views para que o armazenamento seja feito no MongoDB ao invés do SQLite.

No MongoEngine algumas operações serão um pouco diferente, alguns exemplos:

Criar um novo registro de Noticia

Buscar todas as Noticias

Buscar uma noticia pelo id

Altere blueprints/noticias.py para:

Lembre-se que nós ainda não conectamos ao Mongo Server apenas definimos como será a conexão, então precisaremos agora usar o método lazy de inicialização de extensões chamando o init_app() do MongoEngine.

No arquivo news_app.py adicione as seguintes linhas.

Sendo que o arquivo final será:

Execute o programa

E veja se consegue inserir algumas noticias acessando http://localhost:5000

Para explorar os dados do MongoDB visualmente você pode utilizar o RoboMongo.

O Diff das alterações que fizemos relativas ao Flask-MongoEngine podem ser comparadas nos seguintes commits 88effa01b5ffd11f3fd7d5530f90591e421dd109 e 189f4d4d2c8af845ccc0b181e4f6a1831578fbfa

Controle de acesso com o Flask Security

Nosso CMS de notícias está inseguro, ou seja, qualquer um que acessar a url http://localhost:5000/noticias/cadastro vai conseguir adicionar uma nova notícia sem precisar efetuar login.

Para resolver este tipo de problema existe a extensão Flask-Login que oferece métodos auxiliares para autenticar usuários e também a Flask-Security é um pacote feito em cima do Flask-Login (controle de autenticação), Flask-Principal (Controle de Permissões) e Flask-Mail (envio de email).

A vantagem de usar o Flask-Security é que ele já se integra com o MongoEngine e oferece templates prontos para login, alterar senha, envio de email de confirmação etc...

Começaremos adicionando a dependencia ao arquivo de requirements.

requirements.txt

E então instalamos com pip install -r requirements.txt --upgrade

Secret Key

Para encriptar os passwords dos usuários o Flask-Login irá utilizar a chave secret key do settings de seu projeto. É muito importante que esta chave seja segura e gerada de maneira randomica (utilize uuid4 ou outro método de geração de chaves).

Para testes e desenvolvimento você pode utilizar texto puro. mas em produção escolha uma chave segura!

Além disso o Flask-Security precisa que seja especificado qual tipo de hash usar nos passwords.

Adicione ao development_instance/config.cfg

Importante se esta chave for perdida todas as senhas armazenadas serão invalidadas.

Definindo o schema dos usuários e grupos

O Flask-Security permite o controle de acesso utilizando RBAC (Role Based Access Control), ou seja, usuários pertencem a grupos e os acessos são concedidos aos grupos.

Para isso precisamos armazenar (no nosso caso no MongoDB) os usuários e seus grupos.

Crie um novo arquivo security_models.py e criaremos duas classes User e Role

O arquivo acima define os models com todas as propriedades necessárias para que o Flask-Security funcione com o MongoEngine, não entrerei em detalhes de cada campo pois usaremos somente o básico neste tutorial, acesse a documentação do Flask-Security se desejar saber mais a respeitod e cada atributo.

Inicializando o Flask Security em seu projeto

Da mesma forma que fizemos com as outras extensões iremos fazer como security, alterando o arquivo news_app.py e inicializando a extensão utilizando o método default.

Importaremos o Security e o MongoEngineUserDatastore e inicializaremos a extensão passando nossos models de User e Role.

news_app.py

Pronto, agora temos nossa base de usuários e grupos definida e o Security irá iniciar em nosso app todo o restante necessário para o controle de login (session, cookies, formulários etc..)

Exigindo login para cadastro de notícia

Altere a view de cadastro em blueprints/noticias.py e utilize o decorator login_required que é disponibilizado pelo Flask-Security, sendo que o inicio do arquivo ficará assim:

Execute python run.py acesse http://localhost:5000/noticias/cadastro e verifique que o login será exigido para continuar.

NOTE: Se por acaso ocorrer um erro TypeError: 'bool' object is not callable execute o seguinte comando pip install Flask-Login==0.2.11 e adicione Flask-Login==0.2.11 no arquivo requirements.txt. Este erro ocorre por causa de um recente bug na nova versão do Flask-Login.

Se tudo ocorrer como esperado agora você será encaminhado para a página de login.

O único problema é que você ainda não possui um usuário para efetuar o login. Em nosso model de User definimos um método create_user que pode ser utilizado diretamente em um terminal iPython. Porém o Flask-Security facilita bastante fornecendo também um formulário de registro de usuários.

Adicione as seguintes configurações no arquivo development_instance/config.cfg para habilitar o formulário de registro de usuários.

Agora acesse http://localhost:5000/register e você poderá registar um novo usuário e depois efetuar login.

NOTE: É recomendado que a opção de registro de usuário seja desabilidata em ambiente de produção, que seja utilizado outros meios como o Flask-Admin que veremos adiante para registrar novos usuários ou que seja habilitado o Captcha para os formulários de registro e login e também o envio de email de confirmação de cadastro.

Todas as opções de configuração do Flsk-Security estão disponíveis em https://pythonhosted.org/Flask-Security/configuration.html

Agora será interessante mostrar opções de Login, Logout, Alterar senha na barra de navegação. Para isso altere o template base.html adicionando o bloco de access control.

{%- extends "bootstrap/base.html" %}
{% import "bootstrap/utils.html" as utils %}
{% block title %} {{title or "Notícias"}} {% endblock %}
{% block navbar -%}
<nav class="navbar navbar-default">
<a class="navbar-brand" href="#"><img src="{{url_for('static', filename='generic_logo.gif')}}" style="height:30px;"></a>
<ul class="nav navbar-nav">
<li><a href="{{url_for('noticias.index')}}">HOME</a> </li>
<li><a href="{{url_for('noticias.cadastro')}}">CADASTRO</a></li>
{% block access_control %}
<li class="divider-vertical"></li>
{% if current_user.is_authenticated() %}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
{{current_user.email}} <b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="{{url_for_security('change_password')}}"><i class="icon-user"></i> Change password</a></li>
<li><a href="{{url_for_security('logout')}}"><i class="icon-off"></i> Logout</a></li>
</ul>
</li>
{% else %}
<li><a href="{{url_for_security('login')}}"><i class="icon-off"></i> Login</a></li>
{% endif %}
{% endblock %}
</ul>
</nav>
{%- endblock navbar %}
{% block content %}
<div class="container"&

Show more