|
|
|
<!doctype html>
|
|
|
|
<html lang="en">
|
|
|
|
|
|
|
|
<head>
|
|
|
|
<meta charset="utf-8">
|
|
|
|
|
|
|
|
<title>Flask</title>
|
|
|
|
|
|
|
|
<meta name="author" content="Julio Biason">
|
|
|
|
|
|
|
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
|
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
|
|
|
|
|
|
<link rel="stylesheet" href="_external/reveal.min.css">
|
|
|
|
<link rel="stylesheet" href="_external/default.css" id="theme">
|
|
|
|
|
|
|
|
<!-- For syntax highlighting -->
|
|
|
|
<link rel="stylesheet" href="_external/zenburn.css">
|
|
|
|
|
|
|
|
<!-- If the query includes 'print-pdf', include the PDF print sheet -->
|
|
|
|
<script>
|
|
|
|
if( window.location.search.match( /print-pdf/gi ) ) {
|
|
|
|
var link = document.createElement( 'link' );
|
|
|
|
link.rel = 'stylesheet';
|
|
|
|
link.type = 'text/css';
|
|
|
|
link.href = '_external/pdf.css';
|
|
|
|
document.getElementsByTagName( 'head' )[0].appendChild( link );
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<!--[if lt IE 9]>
|
|
|
|
<script src="reveal.js/lib/js/html5shiv.js"></script>
|
|
|
|
<![endif]-->
|
|
|
|
|
|
|
|
<style>
|
|
|
|
.semi-opaque {
|
|
|
|
background-color: rgba(0, 0, 0, 0.7);
|
|
|
|
}
|
|
|
|
|
|
|
|
* {
|
|
|
|
hyphens: none !important;
|
|
|
|
-moz-hyphens: none !important;
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
</head>
|
|
|
|
|
|
|
|
<body>
|
|
|
|
|
|
|
|
<div class="reveal">
|
|
|
|
<!-- Any section element inside of this container is displayed as a slide -->
|
|
|
|
<div class="slides">
|
|
|
|
<section data-background='_images/flask.png'>
|
|
|
|
<h1>Flask</h1>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<h2>O que é Flask?</h2>
|
|
|
|
|
|
|
|
<ul>
|
|
|
|
<li>Microframework web em Python.</li>
|
|
|
|
<li>Framework sobre o Werkzeug (outro framework).</li>
|
|
|
|
<li>Sem ORM, mas templates.</li>
|
|
|
|
</ul>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<section>
|
|
|
|
<h2>Aplicativo Flask Básico</h2>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
from flask import Flask
|
|
|
|
app = Flask(__name__)
|
|
|
|
|
|
|
|
@app.route('/')
|
|
|
|
def index():
|
|
|
|
return 'Hello world'
|
|
|
|
</code></pre></p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
#!/usr/bin/env python
|
|
|
|
# -*- encoding: utf-8 -*-
|
|
|
|
|
|
|
|
from flask import Flask
|
|
|
|
app = Flask(__name__)
|
|
|
|
|
|
|
|
@app.route('/')
|
|
|
|
def index():
|
|
|
|
return 'Hello world'
|
|
|
|
</code></pre></p>
|
|
|
|
<p><small>... mais o header...</small></p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
#!/usr/bin/env python
|
|
|
|
# -*- encoding: utf-8 -*-
|
|
|
|
|
|
|
|
"""Meu aplicativo web em Flask."""
|
|
|
|
|
|
|
|
from flask import Flask
|
|
|
|
app = Flask(__name__)
|
|
|
|
|
|
|
|
@app.route('/')
|
|
|
|
def index():
|
|
|
|
return 'Hello world'
|
|
|
|
</code></pre></p>
|
|
|
|
<p><small>... mais a documentação do módulo...</small></p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
#!/usr/bin/env python
|
|
|
|
# -*- encoding: utf-8 -*-
|
|
|
|
|
|
|
|
"""Meu aplicativo web em Flask."""
|
|
|
|
|
|
|
|
from flask import Flask
|
|
|
|
app = Flask(__name__)
|
|
|
|
|
|
|
|
@app.route('/')
|
|
|
|
def index():
|
|
|
|
"""Apresentação do 'root' do aplicativo."""
|
|
|
|
return 'Hello world'
|
|
|
|
</code></pre></p>
|
|
|
|
|
|
|
|
<p><small>... mais a documentação das funções...</small></p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
#!/usr/bin/env python
|
|
|
|
# -*- encoding: utf-8 -*-
|
|
|
|
|
|
|
|
"""Meu aplicativo web em Flask."""
|
|
|
|
|
|
|
|
from flask import Flask
|
|
|
|
from flask import render_template
|
|
|
|
|
|
|
|
app = Flask(__name__)
|
|
|
|
|
|
|
|
@app.route('/')
|
|
|
|
def index():
|
|
|
|
"""Apresentação do 'root' do aplicativo."""
|
|
|
|
return render_template('hello.html')
|
|
|
|
</code></pre></p>
|
|
|
|
|
|
|
|
<p><small>... mais retornar templates ao invés de texto puro...</small></p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
#!/usr/bin/env python
|
|
|
|
# -*- encoding: utf-8 -*-
|
|
|
|
|
|
|
|
"""Meu aplicativo web em Flask."""
|
|
|
|
|
|
|
|
class Settings:
|
|
|
|
SECRET_KEY = 'Sup3rs3cr33t'
|
|
|
|
|
|
|
|
from flask import Flask
|
|
|
|
from flask import render_template
|
|
|
|
|
|
|
|
app = Flask(__name__)
|
|
|
|
app.config.from_object(Settings)
|
|
|
|
app.config.from_envvar('MEU_APLICATIVO_CONFIG')
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/')
|
|
|
|
def index():
|
|
|
|
"""Apresentação do 'root' do aplicativo."""
|
|
|
|
return render_template('hello.html')
|
|
|
|
</code></pre></p>
|
|
|
|
|
|
|
|
<p><small>... mais adicionar uma configuração...</small></p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p><small>... mais tratamento de erros...</small></p>
|
|
|
|
|
|
|
|
<p class='fragment'><small>... mais outras rotas...</small></p>
|
|
|
|
|
|
|
|
<p class='fragment'><small>... mais blueprints/applications...</small></p>
|
|
|
|
|
|
|
|
<p class='fragment'><small>... mais inicialização do ORM...</small></p>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<img src='_images/baby-steps.jpg'>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<h2>Criando uma app</h2>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
app = Flask(__name__)
|
|
|
|
</code></pre></p>
|
|
|
|
|
|
|
|
<p><code>__name__</code> é usado para que, internamente, os módulos/imports sejam encontrados.</p>
|
|
|
|
|
|
|
|
<p>Se o app estiver em "meuservico/app.py", as duas linhas abaixo funcionam de forma idêntica:<br/>
|
|
|
|
<pre><code>app = Flask(__name__)</code></pre>
|
|
|
|
<pre><code>app = Flask("meuservico")</code></pre>
|
|
|
|
</p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<section>
|
|
|
|
<h2>Contextos</h2>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Essa é a parte chata do Flask.</p>
|
|
|
|
|
|
|
|
<p>Existem dois contextos: Contexto de aplicação e Contexto que requisição.</p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Contexto de aplicação só existe quando o app está rodando.</p>
|
|
|
|
|
|
|
|
<p>Acessado com <code>current_app</code>.</p>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
from flask import current_app
|
|
|
|
</code></pre></p>
|
|
|
|
|
|
|
|
<p>É a única forma de acessar dados da aplicação enquanto ela
|
|
|
|
está rodando.</p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Contexto de requisição só existe quando o sistema está
|
|
|
|
atendendo uma requisção (recebeu uma URL).</p>
|
|
|
|
|
|
|
|
<p>Acessado com <code>request</code>.</p>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
from flask import request
|
|
|
|
</code></pre></p>
|
|
|
|
|
|
|
|
<p>Mais sobre <code>request</code> mais adiante.</p>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<section>
|
|
|
|
<h2>Rotas</h2>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Rotas são definidas com o decorator <code>@[app].route([rota])</code>. Por exemplo:</p>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
app = Flask(__name__)
|
|
|
|
@app.route('/')
|
|
|
|
def index():
|
|
|
|
return 'Olá mundo'
|
|
|
|
</code></pre></p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Rotas também podem definir quais métodos HTTP são aceitos:</p>
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
@app.route('/', methods=['POST', 'GET'])
|
|
|
|
def index():
|
|
|
|
return 'Olá mundo'
|
|
|
|
</code></pre></p>
|
|
|
|
|
|
|
|
<p>"<code>PUT /</code>" irá retornar status 405: Method Not Allowed.</p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Rotas podem ser repetidas, desde que os métodos não colidam:</p>
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
@app.route('/', methods=['GET'])
|
|
|
|
def list():
|
|
|
|
return 'Olá mundo'
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/', methods=['POST'])
|
|
|
|
def update():
|
|
|
|
return 'O seu mundo foi atualizado'
|
|
|
|
</code></pre></p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Rotas podem ter parâmetros:</p>
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
app = Flask(__name__)
|
|
|
|
@app.route('/<usuario>/data')
|
|
|
|
def index(usuario):
|
|
|
|
return 'Olá {nome}'.format(nome=usuario)
|
|
|
|
</code></pre></p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Parâmetros podem ter um tipo definido:</p>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
@app.route('/add/<int:var1>/<int:var2>')
|
|
|
|
def add(var1, var2):
|
|
|
|
return 'Soma = {sum}'.format(sum=var1+var2)
|
|
|
|
</code></pre></p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Problemas: Número de rotas tente a crescer. E como passar <code>app</code> para cima e para baixo?</p>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<section>
|
|
|
|
<h2>Blueprints</h2>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Blueprints são funções relacionadas (por exemplo, pelo recurso base)
|
|
|
|
que podem ser desenvolvidos separados do módulo principal e entre si.</p>
|
|
|
|
|
|
|
|
<p>(Conceito semelhante aos "apps" do Django ou "scaffolding" do Pyramid/Pylons.)</p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<h3>Esqueleto de um Blueprint</h3>
|
|
|
|
|
|
|
|
<p><pre><code>
|
|
|
|
from flask import Blueprint
|
|
|
|
|
|
|
|
blueprint_exemplo = Blueprint('exemplo', __name__)
|
|
|
|
|
|
|
|
@blueprint_exemplo.route('/say/<usuario>')
|
|
|
|
def blueprint_index(usuario):
|
|
|
|
return 'Olá {nome}'.format(nome=usuario)
|
|
|
|
</code></pre></p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<h3>E para ativar o Blueprint...</h3>
|
|
|
|
|
|
|
|
<p><pre><code>
|
|
|
|
app = Flask(__name__)
|
|
|
|
|
|
|
|
from exemplo import blueprint_exemplo
|
|
|
|
app.register(blueprint_exemplo, url_prefix='/exemplo')
|
|
|
|
</code></pre></p>
|
|
|
|
|
|
|
|
<p>A combinação do blueprint anterior com esse carregamento, gera a URL
|
|
|
|
<code>/exemplo/say/<usuario></code>.</p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p><pre><code>
|
|
|
|
blueprint_exemplo = Blueprint('exemplo', __name__)
|
|
|
|
</code></pre></p>
|
|
|
|
|
|
|
|
<ul>
|
|
|
|
<li><code>exemplo</code> é o nome do blueprint (para que isso serve, mais adiante)</li>
|
|
|
|
<li><code>__name__</code> tem a mesma finalidade do <code>__name__</code> do app: encontrar módulos/recursos.</li>
|
|
|
|
</ul>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Tudo que foi visto sobre rotas continua valendo:</p>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
@blueprint_exemplo.route('/<int:var1>/<int:var2>', methods=['GET'])
|
|
|
|
def sum(var1, var2):
|
|
|
|
return 'Soma = {sum}'.format(sum=var1+var2)
|
|
|
|
</code></pre></p>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<section>
|
|
|
|
<h2>Templates</h2>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Flask vem com Jinja2 como renderizado de templates.</p>
|
|
|
|
|
|
|
|
<p>A sintaxe é muito semelhante ao sistema de templates do Django:</p>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
{% for nome in usuarios %}
|
|
|
|
<div class='usuario'>Olá {{ nome }}<div>
|
|
|
|
{% endfor %}
|
|
|
|
</code></pre></p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Para usar um template, basta usar a função <code>render_template</code>.</p>
|
|
|
|
|
|
|
|
<p>Parâmetros para o template devem ser passados como parâmetros adicionais na função:</p>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
from flask import render_template
|
|
|
|
|
|
|
|
@app.index('/')
|
|
|
|
def index():
|
|
|
|
return render_template('hello-world.html',
|
|
|
|
usuarios=['Julio', 'Leandro', 'Bruna', 'Cláudio'])
|
|
|
|
</code></pre></p>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<section>
|
|
|
|
<h2>Responses</h2>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p><code>render_template()</code> é simplesmente um parser de templates com um gerador
|
|
|
|
de Responses.</p>
|
|
|
|
|
|
|
|
<p class='fragment'>(Ou seja, o resultado esperado das funções -- qualquer função -- é um Response.)</p>
|
|
|
|
|
|
|
|
<p class='fragment'>(Criar um response especializado é algo que somente e feito em 2% dos casos.)</p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Por que isso é importante?</p>
|
|
|
|
|
|
|
|
<p class='fragment'>Responses tem algumas propriedades a mais, como o tipo do retorno (text/html, por exemplo)
|
|
|
|
e o status.</p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Para retornar um status diferente de 200:</p>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
def index():
|
|
|
|
resp = render_template('404-not-found.html')
|
|
|
|
resp.status_code = 404
|
|
|
|
return resp
|
|
|
|
</code></pre></p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Para retornar um tipo diferente, é preciso criar o Response do zero:</p>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
def index():
|
|
|
|
resp = make_response('Olá mundo')
|
|
|
|
resp.status_code = 200
|
|
|
|
resp.mimetype = 'text/plain'
|
|
|
|
</code></pre></p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Para JSON, já existe uma função pronta:</p>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
from flask import jsonify
|
|
|
|
|
|
|
|
@app.route('/')
|
|
|
|
def index():
|
|
|
|
usuarios=['Julio', 'Leandro', 'Bruna', 'Cláudio']
|
|
|
|
return jsonify(status='OK',
|
|
|
|
usuarios=usuarios)
|
|
|
|
</code></pre></p>
|
|
|
|
|
|
|
|
<p>Isso gera o JSON</p>
|
|
|
|
<p><code>{status: "OK", usuarios=["Julio", "Leandro", "Bruna", "Claudio"]}</code>.</p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Como <code>jsonify()</code> gera um Response, ele ainda pode ser mexido:</p>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
def index():
|
|
|
|
resp = jsonify(status='ERROR', code='404')
|
|
|
|
resp.status = 404
|
|
|
|
return resp
|
|
|
|
</code></pre></p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Flask também tem funções para auxiliar na geração de respostas que não são "páginas":</p>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
from flask import abort
|
|
|
|
|
|
|
|
def index():
|
|
|
|
abort(404)
|
|
|
|
</code></pre></p>
|
|
|
|
|
|
|
|
<p>Gera um response com status 404 padrão do sistema.</p>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
from flask import redirect
|
|
|
|
|
|
|
|
def index():
|
|
|
|
redirect('/correct-path')
|
|
|
|
</code></pre></p>
|
|
|
|
|
|
|
|
<p>Gera um redirectionamento para <code>/correct-path</code>.</p>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<section>
|
|
|
|
<h2>Requests</h2>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Request contém as informações que estão vindo na requisição:</p>
|
|
|
|
|
|
|
|
<ul>
|
|
|
|
<li><code>request.method</code>: Método HTTP utilizado.</li>
|
|
|
|
<li><code>request.form</code>: Dados do formulário com POST/PUT.</li>
|
|
|
|
<li><code>request.args</code>: Dados do querystring (GET).</li>
|
|
|
|
<li><code>request.values</code>: form + args.</li>
|
|
|
|
<li><code>request.cookies</code>: Cookies da página.</li>
|
|
|
|
<li><code>request.headers</code>: Headers recebidos.</li>
|
|
|
|
<li><code>request.files</code>: Arquivos enviados.</li>
|
|
|
|
<li><code>request.get_json()</code>: Parseia a resposta se ela for JSON.</li>
|
|
|
|
</ul>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
from flask import request # acesso ao objeto de request atual
|
|
|
|
from flask import abort
|
|
|
|
from flask import render_template
|
|
|
|
|
|
|
|
def index():
|
|
|
|
nome = request.values.get('usuario')
|
|
|
|
if not nome:
|
|
|
|
abort(400) # Bad Request
|
|
|
|
|
|
|
|
return render_template('hello-world.html',
|
|
|
|
usuarios=[nome])
|
|
|
|
</code></pre></p>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<section>
|
|
|
|
<h2>Tratamento de erros</h2>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Para mostrar páginas diferentes da default, basta adicionar um <code>errorhandler</code>.</p>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
app = Flask(__name__)
|
|
|
|
|
|
|
|
@app.errorhandler(404)
|
|
|
|
def not_found():
|
|
|
|
return jsonify(status='ERROR',
|
|
|
|
message='Not found')
|
|
|
|
</code></pre></p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p><code>errorhandler</code> também pode capturar excessões:</p>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
from flask import Flask
|
|
|
|
from mongoengine import NotFoundError
|
|
|
|
|
|
|
|
app = Flask(__name__)
|
|
|
|
|
|
|
|
@app.errorhandler(NotFoundError)
|
|
|
|
def not_found():
|
|
|
|
return jsonify(status='ERROR',
|
|
|
|
message='Mongo object not found')
|
|
|
|
</code></pre></p>
|
|
|
|
|
|
|
|
<p>Isso captura qualquer ocorrência de <code>NotFoundError</code>, inclusive
|
|
|
|
dentro dos blueprints.</p>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<section>
|
|
|
|
<h2>Configurações</h2>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Configurações podem vir de 3 lugares diferentes:</p>
|
|
|
|
|
|
|
|
<ul>
|
|
|
|
<li>De uma classe.</li>
|
|
|
|
<li>De um arquivo Python.</li>
|
|
|
|
<li>De um arquivo apontando por uma variável de ambiente.</li>
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
<p>Todos os três podem ser executados em sequência, o último valor
|
|
|
|
encontrado é o que vale.</p>
|
|
|
|
</p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
class Settings(objects):
|
|
|
|
FILE_PATH = './here'
|
|
|
|
ORDER_FIELD = 'name'
|
|
|
|
</code></pre></p>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
app = Flask(__name__)
|
|
|
|
|
|
|
|
app.config.from_object(Settings)
|
|
|
|
app.config.from_pyfile('/etc/meuaplicativo.cfg')
|
|
|
|
app.config.from.envvar('MEUAPLICATIVO_CFG')
|
|
|
|
</code></pre></p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Para acessar as configurações, usa-se a propriedade <code>config</code>
|
|
|
|
da aplicação quando esta está rodando.</p>
|
|
|
|
|
|
|
|
<p>(Contexto de aplicação, lembra?)</p>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
from flask import current_app
|
|
|
|
from flask import render_template
|
|
|
|
|
|
|
|
@app.route('/')
|
|
|
|
def index():
|
|
|
|
return render_template('template.html',
|
|
|
|
order=current_app.config.get('ORDER_FIELD'))
|
|
|
|
</code></pre></p>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<section>
|
|
|
|
<h2>URLs reversas/Endpoints</h2>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Se eu posso registrar um Blueprint com qualquer prefixo, como eu descubro depois qual a URL
|
|
|
|
de um recurso (se eu precisar fazer um redirect)?</p>
|
|
|
|
|
|
|
|
<p class='fragment'><code>url_for()</code></p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p><code>url_for()</code> recebe um endpoint e retorna a URL para aquele endpoint.</p>
|
|
|
|
|
|
|
|
<p class='fragment'>O que diabos é um endpoint?</p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
@app.route('/')
|
|
|
|
def index():
|
|
|
|
return 'Olá mundo'
|
|
|
|
</code></pre></p>
|
|
|
|
|
|
|
|
<p>Endpoint = <code>index</code></p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
exemplo = Blueprint('meuexemplo', __name__)
|
|
|
|
|
|
|
|
@exemplo.route('/')
|
|
|
|
def index():
|
|
|
|
return 'Olá mundo'
|
|
|
|
</code></pre></p>
|
|
|
|
|
|
|
|
<p>Endpoint = <code class='fragment'>meuexemplo.index</code></p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
exemplo = Blueprint('meuexemplo', __name__)
|
|
|
|
|
|
|
|
@exemplo.route('/')
|
|
|
|
def index():
|
|
|
|
return redirect(url_for('meuexemplo.list'))
|
|
|
|
|
|
|
|
@exemplo.route('/list')
|
|
|
|
def list():
|
|
|
|
return 'Olá todos vocês.'
|
|
|
|
</code></pre></p>
|
|
|
|
|
|
|
|
<p>Se registrar o Blueprint com <code>prefix = /exemplo</code>, o <code>index()</code> irá
|
|
|
|
fazer um redirect para <code>/exemplo/list</code>.</p>
|
|
|
|
|
|
|
|
<p>Se registrar o Blueprint com <code>prefix = /</code>, o <code>index()</code> irá
|
|
|
|
fazer um redirect para <code>/list</code>.</p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p><code>url_for()</code> também funciona dentro de templates.</p>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
<button onClick='redirect("{{ url_for('meuexemplo.list') }}")'>
|
|
|
|
</code></pre></p>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<section>
|
|
|
|
<h2>Executando</h2>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Flask vem com um servidor de desenvolvimento integrado.
|
|
|
|
Para utilizar:</p>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
app.run()
|
|
|
|
</code></pre></p>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<p>Ou ainda:</p>
|
|
|
|
|
|
|
|
<p><pre><code data-trim>
|
|
|
|
if __name__ == '__main__':
|
|
|
|
app.run()
|
|
|
|
</code></pre></p>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<section>
|
|
|
|
<h2>Resumo</h2>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section>
|
|
|
|
<ul>
|
|
|
|
<li>Sintaxe simples <span class='fragment'>(a sintaxe foi uma brincadeira de 1o. de abril)</span></li>
|
|
|
|
<li class='fragment'>Controle centralizado de erros.</li>
|
|
|
|
<li class='fragment'>Total controle sobre as respostas. <span class='fragment'>"Explícito é melhor que implícito."</span></li>
|
|
|
|
<li class='fragment'>Acesso total ao request.</li>
|
|
|
|
<li class='fragment'>Módulos completamente isolados<span class='fragment'> mas ainda permite que esses sejam
|
|
|
|
conectados por endpoints.</span></li>
|
|
|
|
<li class='fragment'>Não comentado, mas o Werkzeug também expõe todo o controle da aplicação.</li>
|
|
|
|
<li class='fragment'>Ainda não tem um ORM integrado, mas é fácil de plugar qualquer um.</li>
|
|
|
|
<li class='fragment'>Pythônico.</li>
|
|
|
|
</ul>
|
|
|
|
</section>
|
|
|
|
</section>
|
|
|
|
|
|
|
|
<section data-background='_images/thats-all-folks.jpg'>
|
|
|
|
<section></section>
|
|
|
|
</section>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<script src="_external/head.min.js"></script>
|
|
|
|
<script src="_external/reveal.min.js"></script>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
|
|
// Full list of configuration options available here:
|
|
|
|
// https://github.com/hakimel/reveal.js#configuration
|
|
|
|
Reveal.initialize({
|
|
|
|
controls: true,
|
|
|
|
progress: true,
|
|
|
|
history: true,
|
|
|
|
center: true,
|
|
|
|
|
|
|
|
theme: 'night',
|
|
|
|
transition: 'linear',
|
|
|
|
|
|
|
|
// Optional libraries used to extend on reveal.js
|
|
|
|
dependencies: [
|
|
|
|
{ src: '_external/classList.js', condition: function() { return !document.body.classList; } },
|
|
|
|
{ src: '_external/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
|
|
|
|
{ src: '_external/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
|
|
|
|
{ src: '_external/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
|
|
|
|
{ src: '_external/zoom.js', async: true, condition: function() { return !!document.body.classList; } },
|
|
|
|
{ src: '_external/notes.js', async: true, condition: function() { return !!document.body.classList; } }
|
|
|
|
]
|
|
|
|
});
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
</body>
|
|
|
|
</html>
|