Web Frameworks
Um aplicativo web web application ou simplesmente web app) é um aplicativo executado através de um servidor web, diferente de um aplicativo executado na máquina local, e geralmente rodados e visualizados por meio de um browser ou navegador. Eles podem ser um conjunto de páginas de texto dinâmicas contendo, por exemplo, pesquisas em uma biblioteca, um gerenciador de arquivos na nuvem, um gerenciador de contas bancárias ou emails, um servidor de músicas ou filmes, etc. Com frequência esses aplicativos estão conectados a um banco de dados, podendo fazer neles consultas e modificações.
Um framework para a web é um conjunto de softwares destinados a oferecer suporte ao desenvolvimento de aplicativos na Web. Eles buscam automatizar a construção dos web apps e seu gerenciamento durante o funcionamento, após sua publicação na web. Alguns frameworks web incluem bibliotecas para acesso a banco de dados, templates para a construção dinâmica das páginas e auxílio à reutilização de código. Eles podem facilitar o desenvolvimento de sites dinâmicos ou, em alguns casos, de sites estáticos.
Framework Flask
O Flask é um micro-framework em Python usado para desenvolvimento e gerenciamento de web apps. Ele é considerado micro porque possui poucas dependências para seu funcionamento e pode ser usado com uma estrutura inicial bem básica, voltada para aplicações simples. Apenas duas bibliotecas são instaladas junto com o Flask. Ele não contém, por ex., um gerenciador de banco de dados ou um servidor de email. No entanto esses serviços podem ser acrescentados para ampliar as funcionalidades do aplicativo.
Em particular, o Flask não inclui uma camada de abstração com banco de dados. Em vez disso é possível instalar extensões, escolhendo o banco de dados específico que se quer usar. Essas extensões também podem auxiliar a validação de formulário, manipulação de upload, tecnologias de autenticação aberta, entre outras.
Flask foi desenvolvido por Armin Ronacher e lançado em 2010. Algumas vantagens citadas em seu uso são: (a) projetos escrito com a Flask são simples (comparados àqueles gerados por frameworks maiores, como o Django) e tendem a ser mais rápidos. (b) Ele é ágil e modular: o desenvolvedor se concentra apenas nos aspectos utilizados de seu aplicativo, podendo ampliar sob demanda. (c) Os projetos são pequenos mas robustos. (d) Existe uma vasta comunidade de desenvolvedores contribuindo com seu desenvolvimento, apresentando bibliotecas que ampliam sua funcionalidade.
Criando um aplicativo básico
Nessa primeira parte vamos criar um aplicativo bem básico mas funcional. Depois entraremos em outros detalhes do Flask.
Para criar um projeto usando o Flask (ou, na verdade, outro projeto qualquer) é aconselhável criar antes um ambiente virtual para que os pacotes instalados não conflituem com outros em projetos diferentes. A criação e manutenção de ambientes virtuais está descrita na página Ambientes Virtuais, PIP e Conda. Alguns IDEs, como o Pycharm realizam automaticamente esse processo. No meu caso ele ficará abrigado em ~/Projetos/flask/venv
. Para simplificar denotarei esse ambiente simplesmente pelo nome flask
, que é também o nome da pasta que abriga esse projeto.
Nesse ambiente instalamos o Flask com pip install flask
. Uma estrutura básica de ambiente já estará montada após esse passo. Em seguida criamos um arquivo do python, de nome meu_site.py.
# meu_site.py from flask import Flask app = Flask(__name__) @app.route("/") def homepage(): return "Esta é a minha homepage" if __name__ == "__main__": app.run() # é exibido no console do python * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Esse arquivo não deve se chamar flask.py para evitar conflito de nomes.
A variável __name__, passada para o construtor do Flask contém o nome do módulo principal e é usada para determinar a localização do aplicativo no app. Outros diretórios e arquivos, como pastas de templates, arquivo de estilo e imagens, serão localizadas à partir dessa raiz.
Aplicativos do Flask incluem um servidor de desenvolvimento que pode ser iniciado com o comando run
. Esse comando busca pelo nome do aplicativo na variável de ambiente FLASK_APP
. Se queremos rodar o aplicativo meu_site.py
executamos na linha de comando:
# Linux ou macOS (venv) $ export FLASK_APP=meu_site.py (venv) $ flask run * Serving Flask app "hello" * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) # Microsoft Windows (venv) $ set FLASK_APP=meu_site.py (venv) $ flask run * Serving Flask app "hello" * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) # alternativamente se pode iniciar o servidor com app.run()
No Pycharm, ou outros IDES, você pode executar diretamente esse código.
Da biblioteca flask importamos apenas (por enquanto) a classe Flask. Uma instância da classe é criada com app = Flask(__name__)
onde a variável __name__ contém o nome do projeto. A linha @app.route("/")
é um decorador que informa que a função seguinte será rodada na raiz / do site. Quando esse arquivo .py é executado dentro de uma IDE ou usando python meu_site.py
, na linha de comando, é exibido no console várias mensagens, entre elas a url http://127.0.0.1:5000/, que pode ser clicada ou copiada para a linha de endereço do navegador. Isso resulta na exibição, dentro do navegador, da página:
Clientes e Servidores: O navegador age como o cliente que envia ao servidor uma solicitação, através de uma URL digitada na barra de endereços. O servidor da web transforma essa solicitação em ações a serem realizadas do lado do servidor e retorna uma página com conteúdo de texto e multimídia, renderizados pelo navegador. O Flask fica do lado do servidor, construindo a resposta. Entre outras coisas ele possui um mapeamento entre as URLs e as funções route()
que serão executadas no código *.py.
O endereço e a porta 127.0.0.1:5000 são padrões para o Flask. app.run()
cria um servidor que atende à requisição HTTP do navegador, exibindo a página html. Qualquer texto retornado pela função homepage()
é renderizado no formato html e exibido no navegador. Por exemplo, se fizermos as alterações, colocando o texto entre tags h1:
@app.route("/") def homepage(): return "<h1>Esta é a minha homepage</h1>" if __name__ == "__main__": app.run(debug=True)
o texto agora é renderizado como um título de nível 1:
o mesmo texto será exibido mas agora com formatação de título, a tag h1. Todas as demais tags podem ser utilizadas. O parâmetro debug=True
faz com que alterações no código sejam imediatamente repassadas para as requisições ao servidor, sem a necessidade de rodar todo o projeto novamente. Com isso basta recarregar a página do navegador para que alterações sejam exibidas, clicando no ícone de atualização ou pressionando F5. No mode debug os módulos dois módulos chamados reloader e debugger estão ativados por default. Com o debugger ativado as mensagens de erro são direcionadas para a página exibida. O mode debug nunca deve ser ativado em um servidor em produção pois isso fragiliza a segurança do site.
Também podemos ativar o módulo no código que executa o aplicativo:
(venv) $ export FLASK_APP=meu_site.py (venv) $ export FLASK_DEBUG=1 (venv) $ flask run
O decorador @app.route("/")
registra a função homepage()
junto com a página raiz do site. Outras páginas vão executar outras funções. Por exemplo, uma página de contatos pode ser inserida por meio da inserção de nova função no código. Nesse caso criaremos a função contatos()
.
# meu_site.py from flask import Flask app = Flask(__name__) @app.route("/") def homepage(): return "<h1>Esta é a minha homepage</h1>" @app.route("/contatos") def contatos(): txt = ( "<h1>Página de Contatos</h1>" "<ul>" "<li>Contato 1</li>" "<li>Contato 2</li>" "</ul>" ) return txt if __name__ == "__main__": app.run(debug=True)
Usamos acima a concatenação de string com parênteses: (str1 str2 ... strn)
.
Agora, além da homepage temos a página de contatos em 127.0.0.1:5000/contatos, com a seguinte aparência.
A funções contatos()
e homegage()
são chamadas funções de visualização (view functions).
Html e templates: Notamos agora que o código em meu_site.py contém sintaxe misturada de Python e html e pode ficar bem complexo em uma página real exibida na web. Para evitar isso o Flask permite a criação de templates. Fazemos isso da seguinte forma: no diretório raiz onde está o projeto (o mesmo onde foi gravado meu_site.py) criamos o diretório template (o nome default do Flask). Dentro dele colocamos nossos templates. Por exemplo, criamos os arquivos homepage.html,
# homepage.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Homepage: teste Flask</title> </head> <body> <h1>Este é o título da Homepage</h1> <p>Com os devidos parágrafos...</p> </body> </html>
e contatos.html:
# contatos.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Contatos</title> </head> <body> <h1>Página de Contatos</h1> <ul> <li>Contato 1</li> <li>Contato 2</li> </ul> </body> </html>
Vários IDEs podem auxiliar na criação desses arquivos html, fornecendo um esqueleto básico a ser preenchido pelo programador.
Além disso modificamos nosso código python para usar a renderização dos templates, importando render_template
.
# meu_site.py from flask import Flask, render_template app = Flask(__name__) @app.route("/") def homepage(): return render_template("homepage.html") @app.route("/contatos") def contatos(): return render_template("contatos.html") if __name__ == "__main__": app.run(debug=True)
Quando esse código é executado temos a referência ao link que, se aberto, mostra as páginas criadas: digitando 127.0.0.1:5000 abrimos nossa homepage:
Mas, se digitarmos 127.0.0.1:5000/contatos a outra página é exibida:
Uma página pode receber parâmetros do código em python. Por exemplo, digamos que queremos exibir uma página para cada produto existente em uma loja virtual que vende frutas. Nesse caso acrescentamos no código de meu_site.py:
@app.route("/frutas/<nome_da_fruta>") def frutas(nome_da_fruta): return render_template("frutas.html")
Para receber esse parâmetro temos que gravar a página frutas.html na pasta templates, com um conteúdo que receba essa variável.
# frutas.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Frutas disponíveis</title> </head> <body> <h1>Frutas</h1> <p>Você escolheu a fruta: {{nome_da_fruta}}</p> </body> </html>
Se for digitado no campo de endereços do navegador, ou passado por meio de um link na tag <a href="http://127.0.0.1:5000/frutas/laranja">Laranja</a>
a parte do endereço <nome_da_fruta> = laranja
é passado como valor de parâmetro na função frutas("laranja")
que é disponibilizado dentro do código html como {{nome_da_fruta}}
.
Resumindo: @app.route("/frutas/<nome_da_fruta>")
envia uma string na variável nome_da_fruta
para a função frutas que, por sua vez repassa ao código html. Dentro do html a variável fica disponível como {{nome_da_fruta}} (dentro de uma dupla chave).
Por exemplo, se digitamos na barra de endereços do navegador http://127.0.0.1:5000/frutas/laranja
teremos a exibição de
Essa técnica pode ser usada, por ex., para criar páginas para diversos usuários usando um único template usuario.html
.
@app.route('/usuario/<nome>') def usuario(nome): return render_template("usuario.html") # ou @app.route('/usuario/<int:id>') return render_template("usuario.html")
A parte do código <int:id> é um filtro que transforma a entrada digitada em inteiro, quando possível e será melhor explicada adiante.
Formatando com CSS
O texto dentro de uma página html (HyperText Markup Language) pode ser formatado de algumas formas diferentes, usando css (Cascading Style Sheets). Quando se trata do uso de um framework a forma preferida consiste em apontar no cabeçalho para um arquivo externo css. No Flask isso é feito da seguinte forma: um arquivo css é gravado na pasta static
da pasta do projeto. Digamos que gravamos o arquivo /static/styles.css
com um conteúdo mínimo, apenas para demonstração, tornando vermelhas as letras do título e azuis as letras dos parágrafos:
# arquivo /static/styles.css
h1 { color:red; }
p { color:blue; }
No cabeçalho das páginas html, dentro da tag <head> colocamos um link para o arquivo de estilos:
# homepage.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="stylesheet" type="text/css" href="static/styles.css"> <title>Homepage: teste Flask</title> </head>
Agora, ao acessar a homepage
veremos:
Com todas essas alterações o projeto tem agora a estrutura de pastas mostrada. Na figura à esquerda todas as pastas, inclusive aquelas criadas pelo gerenciador de ambientes virtuais são mostradas. No meu caso elas foram criadas automaticamente pelo IDE Pycharm, mas podem ser criadas pelo programador sem dificuldade. Na figura à direita são mostradas apenas as pastas criadas pela programador diretamente. Um projeto com esse formato roda perfeitamente, apesar de não contar com as vantagens do ambiente virtual (veja artigo).
Outras estruturas de código podem ser inseridas nos templates, como veremos.
Opções de comando de linha
Quando se roda o flask diretamente no terminal podemos ver uma mensagem de ajuda com (venv) $ flask --help
, verificar os caminhos definidos no app ou entrar em uma shell interativa.
# Exibir ajuda do flask (venv) $ flask --help Usage: flask [OPTIONS] COMMAND [ARGS]... A general utility script for Flask applications. Provides commands from Flask, extensions, and the application. Loads the application defined in the FLASK_APP environment variable, or from a wsgi.py file. Setting the FLASK_ENV environment variable to 'development' will enable debug mode. $ export FLASK_APP=hello.py $ export FLASK_ENV=development $ flask run Options: --version Show the flask version --help Show this message and exit. Commands: routes Show the routes for the app. run Run a development server. shell Run a shell in the app context. # exibe os caminhos ativos no aplicativo (venv) $ flask routes Endpoint Methods Rule -------- ------- ----------------------- contatos GET /contatos/ frutas GET /frutas/ frutas GET /frutas/ homepage GET / static GET /static/ # entra em uma shell interativa (venv) $ flask shell
A shell do flask inicia uma sessão python no contexto do atual aplicativo onde podemos executar testes ou tarefas de manutenção. O comando flask run
admite vários parâmetros:
(venv) $ flask run --help Usage: flask run [OPTIONS] Run a local development server. This server is for development purposes only. It does not provide the stability, security, or performance of production WSGI servers. The reloader and debugger are enabled by default if FLASK_ENV=development or FLASK_DEBUG=1. Options: -h, --host TEXT The interface to bind to. -p, --port INTEGER The port to bind to. --cert PATH Specify a certificate file to use HTTPS. --key FILE The key file to use when specifying a certificate. --reload / --no-reload Enable or disable the reloader. By default the reloader is active if debug is enabled. --debugger / --no-debugger Enable or disable the debugger. By default the debugger is active if debug is enabled. --eager-loading / --lazy-loading Enable or disable eager loading. By default eager loading is enabled if the reloader is disabled. --with-threads / --without-threads Enable or disable multithreading. --extra-files PATH Extra files that trigger a reload on change. Multiple paths are separated by ':'. --help Show this message and exit.
O argumento --host
informa ao servido qual é o ambiente web que pode acessar nosso servidor de desenvolvimento. Por default o servidor de desenvovimento do Flask só aceita chamadas do computador local, em localhost
. Mas é possível configurá-lo para receber chamadas da rede local ou de ambientes mais amplos. Por exemplo, como o código
(venv) $ flask run --host 0.0.0.0 * Serving Flask app "hello" * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
todos os computadores conectados pelo mesmo ip terão acesso ao aplicativo.
Implantação de um aplicativo Flask
O processo de implantação (ou deploy) de um aplicativo consiste nas etapas necessários para colocá-lo acessível para seus usuários. No caso de um aplicativo web a implantação significa estabelecer um servidor ou usar servidores já disponíveis, que os usuários possam acessar, e colocar seu aplicativo como um de seus serviços.
O desenvolvimento do aplicativo se dá em um ambiente de desenvolvimento onde podem existir condições próprias para o debug e nem todas as medidas de segurança estão implementadas. Depois ele passa para a etapa de uso, no ambiente de produção. Uma conta no Heroku pode ser criada, e um site com poucos acessos pode ser mantido sem custos. Se o site for escalonado e crescer a conta deve ser atualizada e paga. A lista abaixo contém links para o Heroku e outros provedores.
- Implantação no Heroku
- Implantação no Google App Engine
- Implantação no Google Cloud Run
- Implantação no AWS Elastic Beanstalk
- Implantação no Azure (IIS)
- Implantação no PythonAnywhere
Bibliografia
Livros sobre Flask
- Aggarwal, Shalabh: Flask Framework Cookbook, 2.Ed., Packt, Birmingham-Mumbai, 2019.
- Ashley, David: Foundation Dynamic Web Pages with Python Create Dynamic Web Pages with Django and Flask, Apress, 2020.
- Gaspar, D.;StoufferHaider, J.: Mastering Flask Web Development, 2.Ed., Packt, Birmingham-Mumbai, 2018.
- Grinberg, Miguel: The Flask Mega-Tutorial, Edição do autor, 2020.
- Grinberg, Miguel: Flask Web Development, Developing Web Applications with Python, O’Reilly, Sebastopol, 2018.
- Haider, Rehan: Web API Development With Python, CloudBytes, 2020.
- Maia, Italo: Building Web Applications with Flask, 2.Ed., Packt, Birmingham-Mumbai, 2015.
- Relan, Kunal: Building REST APIs with Flask, 2.Ed., Apress, 2019.
Referências na Web
- Flask homepage: Flask Documentation,
- Pythonbasics: What is Flask Python,
- Full Stack Python: Flask, Web Development,
Sobre HTML e CSS
- Html.com, HTML For Beginners The Easy Way,
- Matthew Taylor, Web Design: Add CSS to HTML,
- Moz://a Web Docs, Learn CSS,
- W3 Schools, W3 Schools: html & css,
Olá, criei uma aplicação Flask bem básica contendo uma rota “/” que executa uma função Index que contém um print(“rota principal”) e um retorno de uma string. Ao observar a saída no console, percebi que a função Index está sendo executada duas vezes pois o print foi impresso duas vezes. Como resolver esse problema?