Aula 9
- Bem-vindo!
- Estática para dinâmica
- Flask
- Layout
- POST
- Frosh IMs
- Flask e SQL
- Sessão
- Armazenar
- API
- JSON
- Resumindo
Bem-vindo!
- Nas semanas anteriores, você aprendeu várias linguagens de programação, técnicas e estratégias.
- Na verdade, esta aula tem sido muito menos uma aula de C ou aula de Python e muito mais uma aula de programação, para que você possa seguir tendências futuras.
- Nas últimas semanas, você aprendeu como aprender sobre programação.
- Hoje, mudaremos de HTML e CSS para combinar HTML, CSS, SQL, Python e JavaScript para que você possa criar seus próprios aplicativos da web.
Estática para dinâmica
- Até este momento, todo HTML que você viu foi pré-escrito e estático.
- No passado, quando você visitava uma página, o navegador baixava uma página HTML e você poderia visualizá-la.
- As páginas dinâmicas referem-se à capacidade do Python e linguagens semelhantes de criar arquivos HTML em tempo real. Assim, você pode ter páginas da Web geradas por opções selecionadas pelo usuário.
- Você usou o
http-serveranteriormente para fornecer suas páginas da Web. Hoje, usaremos um novo servidor capaz de analisar um endereço da Web e executar ações com base na URL fornecida.
Flask
- Flask é uma biblioteca de terceiros que permite hospedar aplicativos da Web usando o framework Flask no Python.
- Você pode executar o flask executando
flask run. - Para isso, você precisará de um arquivo chamado
app.pye uma pasta chamadatemplates. -
Para começar, crie uma pasta chamada
templatese crie um arquivo chamadoindex.htmlcom o seguinte código:<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="initial-scale=1, width=device-width"> <title>hello</title> </head> <body> hello, {{ name }} </body> </html>Observe o duplo
{{ name }}que é um placeholder para algo que será fornecido posteriormente por nosso servidor Flask. -
Em seguida, na mesma pasta em que a pasta
templatesaparece, crie um arquivo chamadoapp.pye adicione o seguinte código:# Cumprimenta o usuário from flask import Flask, render_template, request app = Flask(__name__) @app.route("/") def index(): return render_template("index.html", name=request.args.get("name", "world"))Observe que este código define
appcomo o aplicativo Flask. Em seguida, define a rota/deappcomo o retorno do conteúdo deindex.htmlcom o argumento dename. Por padrão, a funçãorequest.args.getprocurará onamefornecido pelo usuário. Se nenhum nome for fornecido, ele será definido comoworld. -
Por fim, adicione um arquivo final na mesma pasta de
app.pychamadorequirements.txtque tem apenas uma única linha de código:FlaskObserve que somente
Flaskaparece neste arquivo. -
Você pode executar esse arquivo digitando
flask runna janela do terminal. Caso o Flask não execute, verifique se sua sintaxe está correta em cada um dos arquivos acima. Além disso, se o Flask não for executado, verifique se seus arquivos estão organizados da seguinte forma:/modelos index.html app.py requirements.txtAssim que conseguir executar, você será solicitado a clicar em um link. Quando você navegar até essa página da web, tente adicionar
?name=[SeuNome]à URL base na barra de URL do seu navegador. -
Melhorando nosso programa, sabemos que a maioria dos usuários não digitará argumentos na barra de endereços. Em vez disso, os programadores contam com os usuários para preencher formulários em páginas da web. Da mesma forma, podemos modificar o index.html da seguinte maneira:
<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="initial-scale=1, width=device-width"> <title>olá</title> </head> <body> <form action="/greet" method="get"> <input autocomplete="off" autofocus name="name" placeholder="Nome" type="text"> <button type="submit">Cumprimentar</button> </form> </body> </html>Observe que um formulário agora é criado que pega o nome do usuário e então o passa para uma rota chamada
/greet. -
Além disso, podemos alterar
app.pyda seguinte forma:# Adiciona um formulário, segunda rota from flask import Flask, render_template, request app = Flask(__name__) @app.route("/") def index(): return render_template("index.html") @app.route("/greet") def greet(): return render_template("greet.html", name=request.args.get("name", "mundo"))Observe que o caminho padrão exibirá um formulário para o usuário inserir seu nome. A rota
/greetpassaránamepara essa página da web. -
Para finalizar essa implementação, você precisará de outro modelo para
greet.htmlda seguinte forma:<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="initial-scale=1, width=device-width"> <title>olá</title> </head> <body> olá, {{ name }} </body> </html>Observe que essa rota agora exibirá a saudação para o usuário, seguida por seu nome.
Layout
- Ambas as nossas páginas da web,
index.htmlegreet.html, têm muitos dados em comum. Não seria ótimo permitir que o corpo fosse único, mas copiar o mesmo layout de página para página? -
Primeiro, crie um novo template chamado
layout.htmle escreva o código da seguinte forma:<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="initial-scale=1, width=device-width"> <title>hello</title> </head> <body> {% block body %}{% endblock %} </body> </html>Observe que
{% block body %}{% endblock %}permite a inserção de outro código de outros arquivos HTML. -
Em seguida, modifique seu
index.htmlda seguinte forma:{% extends "layout.html" %} {% block body %} <form action="/greet" method="post"> <input autocomplete="off" autofocus name="name" placeholder="Name" type="text"> <button type="submit">Greet</button> </form> {% endblock %}Observe que a linha
{% extends "layout.html" %}informa ao servidor onde obter o layout desta página. Em seguida,{% block body %}{% endblock %}informa qual código deve ser inserido emlayout.html. -
Finalmente, altere
greet.htmlda seguinte forma:{% extends "layout.html" %} {% block body %} hello, {{ name }} {% endblock %}Observe como este código é mais curto e compacto.
POST
- Você pode imaginar cenários em que não é seguro utilizar
get, pois nomes de usuários e senhas apareceriam na URL. -
Podemos utilizar o método
postpara ajudar neste problema modificandoapp.pyda seguinte forma:# Altera para POST from flask import Flask, render_template, request app = Flask(__name__) @app.route("/") def index(): return render_template("index.html") @app.route("/greet", methods=["POST"]) def greet(): return render_template("greet.html", name=request.form.get("name", "world"))Observe que
POSTé adicionado à rota/greet, e que usamosrequest.form.getem vez derequest.args.get. -
Isso informa ao servidor para procurar mais profundamente no envelope virtual e não revelar os itens em
postna URL. -
Ainda assim, esse código pode ser refinado ainda mais, utilizando uma rota única para
getepost. Para fazer isso, modifiqueapp.pyda seguinte forma:# Usa uma rota única from flask import Flask, render_template, request app = Flask(__name__) @app.route("/", methods=["GET", "POST"]) def index(): if request.method == "POST": return render_template("greet.html", name=request.form.get("name", "world")) return render_template("index.html")Observe que
getepostsão feitos em um único roteamento. No entanto,request.methodé utilizado para rotear adequadamente com base no tipo de roteamento solicitado pelo usuário.
Frosh IMs
- O Frosh IMs ou froshims é um aplicativo da web que permite que os alunos se inscrevam em esportes intercolegiais.
-
Crie uma pasta digitando
mkdir froshimsna janela do terminal. Em seguida, digitecd froshimspara navegar até essa pasta. Dentro dela, crie um diretório chamado models digitandomkdir templates. Por fim, digitecode app.pye escreva o código como segue:# Implementa um formulário de inscrição usando um menu de seleção de flask import Flask, render_template, request app = Flask(__name__) ESPORTES = [ "Basquete", "Futebol", "Ultimate Frisbee" ] @app.route("/") def index(): return render_template("index.html", esportes=ESPORTES) @app.route("/registrar", methods=["POST"]) def registrar(): # Validade a submissão se não request.form.get("nome") ou request.form.get("esporte") não em ESPORTES: return render_template("falha.html") # Confirme o registro return render_template("sucesso.html")Observe que uma opção de
falhaé fornecida, de modo que uma mensagem de falha será exibida ao usuário se o camponomeouesportenão for preenchido corretamente. -
Em seguida, crie um arquivo na pasta
templateschamadoindex.htmldigitandocode templates/index.htmle escreva o código da seguinte forma:{% extends "layout.html" %} {% bloco body %} <h1>Registrar</h1> <form action="/register" method="post"> <input autocomplete="off" autofocus name="name" placeholder="Nome" type="text"> <select name="sport"> <option disabled selected>Esporte</option> {% for sport in sports %} <option value="{{ sport }}">{{ sport }}</option> {% endfor %} </select> <button type="submit">Registrar</button> </form> {% endblock %} -
Em seguida, crie um arquivo chamado
layout.htmldigitandocode templates/layout.htmle escreva o código da seguinte forma:<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="initial-scale=1, width=device-width"> <title>froshims</title> </head> <body> {% bloco body %}{% endblock %} </body> </html> -
Em quarto lugar, crie um arquivo nos modelos chamado
success.htmlda seguinte forma:{% extends "layout.html" %} {% bloco body %} Você está registrado! {% endblock %} -
Por fim, crie um arquivo nos modelos chamado
failure.htmlda seguinte forma:{% extends "layout.html" %} {% bloco body %} Você não está registrado! {% endblock %} -
Você pode imaginar como queremos aceitar o registro de muitos registradores diferentes. Podemos melhorar o
app.pyda seguinte maneira:# Implementa um formulário de registro, armazenando os registradores em um dicionário, com mensagens de erro from flask import Flask, redirect, render_template, request app = Flask(__name__) REGISTRANTS = {} SPORTS = [ "Basquete", "Futebol", "Ultimate Frisbee" ] @app.route("/") def index(): return render_template("index.html", sports=SPORTS) @app.route("/register", methods=["POST"]) def register(): # Valida o nome name = request.form.get("name") if not name: return render_template("error.html", message="Nome ausente") # Valida o esporte sport = request.form.get("sport") if not sport: return render_template("error.html", message="Esporte ausente") if sport not in SPORTS: return render_template("error.html", message="Esporte inválido") # Lembra do registrador REGISTRANTS[name] = sport # Confirma o registro return redirect("/registrants") @app.route("/registrants") def registrants(): return render_template("registrants.html", registrants=REGISTRANTS)Observe que um dicionário chamado
REGISTRANTSé usado para registrar osportselecionado porREGISTRANTS[name]. Além disso, observe queregistrants=REGISTRANTSpassa o dicionário para esse template. -
Além disso, crie um novo template chamado
registrants.htmlda seguinte maneira:{% extends "layout.html" %} {% block body %} <h1>Registrantes</h1> <table> <thead> <tr> <th>Nome</th> <th>Esporte</th> </tr> </thead> <tbody> {% for name in registrants %} <tr> <td>{{ name }}</td> <td>{{ registrants[name] }}</td> </tr> {% endfor %} </tbody> </table> {% endblock %}Observe que
{% for name in registrants %}...{% endfor %}irá iterar por cada um dos registrantes. Muito poderoso para conseguir iterar em uma página da web dinâmica! -
Executando
flask rune inserindo vários nomes e esportes, você pode navegar até/registrantspara ver quais dados foram registrados. - Você tem agora um aplicativo da web! No entanto, existem algumas falhas de segurança! Por estar tudo no lado do cliente, um adversário poderia alterar o HTML e hackear um site. Além disso, esses dados não persistirão caso o servidor seja desligado. Existe alguma maneira de fazer com que nossos dados persistam mesmo quando o servidor for reiniciado?
Flask e SQL
- Da mesma forma que vimos como o Python pode se conectar a um banco de dados SQL, podemos combinar o poder do Flask, Python e SQL para criar um aplicativo web onde os dados serão persistentes!
- Para implementar isso, você precisará seguir algumas etapas.
-
Primeiro, modifique
requirements.txtcomo a seguir:cs50 Flask -
Modifique
index.htmlcomo a seguir:{% extends "layout.html" %} {% block body %} <h1>Registrar</h1> <form action="/register" method="post"> <input autocomplete="off" autofocus name="name" placeholder="Nome" type="text"> {% for sport in sports %} <input name="sport" type="radio" value="{{ sport }}"> {{ sport }} {% endfor %} <button type="submit">Registrar</button> </form> {% endblock %} -
Modifique
layout.htmlcomo a seguir:<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="initial-scale=1, width=device-width"> <title>froshims</title> </head> <body> {% block body %}{% endblock %} </body> </html> -
Verifique se
failure.htmlé mostrado como a seguir:{% extends "layout.html" %} {% block body %} Você não está registrado! {% endblock %} -
Modifique
registrants.htmlpara ser mostrado como a seguir:{% extends "layout.html" %} {% block body %} <h1>Registrados</h1> <table> <thead> <tr> <th>Nome</th> <th>Esporte</th> <th></th> </tr> </thead> <tbody> {% for registrant in registrants %} <tr> <td>{{ registrant.name }}</td> <td>{{ registrant.sport }}</td> <td> <form action="/deregister" method="post"> <input name="id" type="hidden" value="{{ registrant.id }}"> <button type="submit">Cancelar registro</button> </form> </td> </tr> {% endfor %} </tbody> </table> {% endblock %} -
Observe que um valor oculto
registrant.idé incluído para que seja possível usar oiddepois emapp.py
Por fim, modifique app.py conforme segue:
```python
Implementa um formulário de registro, armazena registrados em um banco de dados SQLite, com suporte para cancelamento de registro
from cs50 import SQL from flask import Flask, redirect, render_template, request
app = Flask(name)
db = SQL("sqlite:///froshims.db")
SPORTS = [ "Basquetebol", "Futebol", "Ultimate Frisbee" ]
@app.route("/") def index(): return render_template("index.html", sports=SPORTS)
@app.route("/deregister", methods=["POST"]) def deregister():
# Esquece o registrado
id = request.form.get("id")
if id:
db.execute("DELETE FROM registrants WHERE id = ?", id)
return redirect("/registrants")
@app.route("/register", methods=["POST"]) def register():
# Valida envio
name = request.form.get("name")
sport = request.form.get("sport")
if not name or sport not in SPORTS:
return render_template("failure.html")
# Lembra do registrado
db.execute("INSERT INTO registrants (name, sport) VALUES(?, ?)", name, sport)
# Confirma registro
return redirect("/registrants")
@app.route("/registrants") def registrants(): registrants = db.execute("SELECT * FROM registrants") return render_template("registrants.html", registrants=registrants) ```
Observe que a biblioteca cs50 é utilizada. Uma rota para register** é incluída para o método post. Essa rota pegará o nome e esporte obtidos a partir do formulário de registro e executará uma consulta SQL para adicionar o nome e o esporte à tabela registrants. A rota deregister direciona para uma consulta SQL que obterá o id do usuário e utilizará essa informação para cancelar o registro desse indivíduo.
Você pode saber mais na documentação do Flask.
Sessão
- Enquanto o código acima é útil sob um ponto de vista administrativo, onde um administrador de back-office pode adicionar e remover usuários do banco de dados, pode-se imaginar como este código não é seguro para implementar num servidor público.
- De um lado, agentes maliciosos poderiam tomar decisões em nome de outros usuários ao clicar no botão de cancelamento de cadastro - excluindo efetivamente sua resposta gravada do servidor.
- Serviços da web como o Google usam credenciais de login para garantir que os usuários tenham acesso apenas aos dados certos.
- Na verdade, podemos implementar isso usado cookies. Cookies são arquivos pequenos que são armazenados em seu computador, assim sendo o seu computador pode se comunicar com o servidor e efetivamente dizer, "Sou um usuário autorizado que já fez login."
- Na forma mais simples, podemos implementar isso criando uma pasta chamada
logine então adicionando os seguintes arquivos. -
Primeiro, crie um arquivo chamado
requirements.txtque lê como a seguir:Flask Flask-SessionObserve que além de
Flask, também incluímosFlask-Session, que é necessário para suportar sessões de login. -
Segundo, numa pasta
templates, crie um arquivo chamadolayout.htmlque aparece como segue:<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="initial-scale=1, width=device-width"> <title>store</title> </head> <body> {% block body %}{% endblock %} </body> </html>Observe que isso oferece um layout muito simples com um título e um corpo.
-
Terceiro, crie um arquivo na pasta
templateschamadoindex.htmlque aparece como segue:{% extends "layout.html" %} {% block body %} {% if session["name"] %} Você está logado como {{ session["name"] }}. <a href="/logout">Sair</a>. {% else %} Você não está logado. <a href="/login">Logar</a>. {% endif %} {% endblock %}Observe que este arquivo procura ver se
session["name"]existe. Se existir, ele mostrará uma mensagem de boas-vindas. Se não existir, recomendará que você navegue até uma página para fazer login. -
Em quarto lugar, crie um arquivo com o nome
login.htmle adicione o código a seguir:{% extends "layout.html" %} {% block body %} <form action="/login" method="post"> <input autocomplete="off" autofocus name="name" placeholder="Nome" type="text"> <button type="submit">Fazer login</button> </form> {% endblock %}Note que esse é o layout de uma página básica de login.
-
Por fim, crie um arquivo na pasta
loginchamadoapp.pye escreva o código como se segue:from flask import Flask, redirect, render_template, request, session from flask_session import Session # Configure o app app = Flask(__name__) # Configure a sessão app.config["SESSION_PERMANENT"] = False app.config["SESSION_TYPE"] = "filesystem" Session(app) @app.route("/") def index(): if not session.get("name"): return redirect("/login") return render_template("index.html") @app.route("/login", methods=["GET", "POST"]) def login(): if request.method == "POST": session["name"] = request.form.get("name") return redirect("/") return render_template("login.html") @app.route("/logout") def logout(): session["name"] = None return redirect("/")Note os imports modificados na parte superior do arquivo, incluindo
session, que permitirá que você use sessões. Mais importante, note comosession["name"]é usado nas rotasloginelogout. A rotaloginatribuirá o nome de login fornecido e o atribuirá asession["name"]. No entanto, na rotalogout, o logout é implementado simplesmente configurandosession["name"]comoNone. -
Você pode ler mais sobre sessões na documentação do Flask.
Armazenamento
- Passando para um exemplo final de utilização da capacidade do Flask de habilitar uma sessão.
- Examinamos o código a seguir para
Armazenamentoemapp.py. O código a seguir foi mostrado:
```python from cs50 import SQL from flask import Flask, redirect, render_template, request, session from flask_session import Session
Configurar aplicativo
app = Flask(name)
Conectar ao banco de dados
db = SQL("sqlite:///store.db")
Configurar sessão
app.config["SESSION_PERMANENT"] = False app.config["SESSION_TYPE"] = "filesystem" Session(app)
@app.route("/") def index(): books = db.execute("SELECT * FROM books") return render_template("books.html", books=books)
@app.route("/cart", methods=["GET", "POST"]) def cart():
# Garantir que o carrinho exista
if "cart" not in session:
session["cart"] = []
# POST
if request.method == "POST":
id = request.form.get("id")
if id:
session["cart"].append(id)
return redirect("/cart")
# GET
books = db.execute("SELECT * FROM books WHERE id IN (?)", session["cart"])
return render_template("cart.html", books=books)
```
Note que cart é implementado usando uma lista. Os itens podem ser adicionados a esta lista usando os botões Adicionar ao Carrinho em books.html. Ao clicar em tal botão, o método post é invocado, onde o id do item é anexado ao cart. Ao visualizar o carrinho, invocando o método get, o SQL é executado para exibir uma lista dos livros no carrinho.
API
- Uma interface de programação de aplicativo ou API é uma série de especificações que permite que você se conecte com outro serviço. Por exemplo, podemos utilizar a API do IMDB para nos conectarmos com o banco de dados deles. Podemos até integrar APIs para controlar tipos específicos de dados baixáveis de um servidor.
- Vimos um exemplo chamado
shows. -
Ao analisar
app.py, vimos o seguinte:# Pesquisa programas usando Ajax from cs50 import SQL from flask import Flask, render_template, request app = Flask(__name__) db = SQL("sqlite:///shows.db") @app.route("/") def index(): return render_template("index.html") @app.route("/search") def search(): q = request.args.get("q") if q: shows = db.execute("SELECT * FROM shows WHERE title LIKE ? LIMIT 50", "%" + q + "%") else: shows = [] return render_template("search.html", shows=shows)Observe que a rota
searchexecuta uma consulta SQL. -
Ao observar
search.html, você notará que é muito simples:{% for show in shows %} <li>{{ show["title"] }}</li> {% endfor %}Observe que ele fornece uma lista com marcadores.
-
Por fim, ao analisar
index.html, observe que um código AJAX é utilizado para impulsionar a pesquisa:<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="initial-scale=1, width=device-width"> <title>shows</title> </head> <body> <input autocomplete="off" autofocus placeholder="Pesquisa" type="search"> <ul></ul> <script> let input = document.querySelector('input'); input.addEventListener('input', async function() { let response = await fetch('/search?q=' + input.value); let shows = await response.text(); document.querySelector('ul').innerHTML = shows; }); </script> </body> </html>Observe que um ouvinte de evento é utilizado para consultar dinamicamente o servidor para fornecer uma lista que corresponda ao título fornecido. Isso localizará a tag
ulno HTML e modificará a página da Web de acordo para incluir a lista de correspondências. -
Você pode obter mais informações na documentação do AJAX.
JSON
- JavaScript Object Notation ou JSON é um arquivo de texto de dicionários com chaves e valores. Essa é uma maneira bruta e amigável ao computador de obter muitos dados.
- JSON é uma forma muito útil de obter dados do servidor.
-
Você pode ver isso em ação em
index.htmlque examinamos juntos:<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="initial-scale=1, width=device-width"> <title>shows</title> </head> <body> <input autocomplete="off" autofocus placeholder="Query" type="text"> <ul></ul> <script> let input = document.querySelector('input'); input.addEventListener('input', async function() { let response = await fetch('/search?q=' + input.value); let shows = await response.json(); let html = ''; for (let id in shows) { let title = shows[id].title.replace('<', '<').replace('&', '&'); html += '<li>' + title + '</li>'; } document.querySelector('ul').innerHTML = html; }); </script> </body> </html>Enquanto o acima pode ser algo críptico, ele oferece um ponto de partida para você pesquisar por JSON por conta própria para ver como ele pode ser implementado em seus próprios aplicativos web.
-
Você pode ler mais na documentação JSON.
Resumo
Nesta lição, você aprendeu como utilizar Python, SQL e Flask para criar aplicativos web. Especificamente, discutimos…
- GET
- POST
- Flask
- Session
- AJAX
- JSON
Vejo vocês na próxima vez para nossa aula final!