├── imagens
├── CMD.png
├── Avatar.png
├── Tabelas.png
├── FlaskLogo.png
├── HTMLForm.png
└── FlaskTemplate.png
├── LICENSE
├── README.md
└── capitulos
├── Referências.md
├── Arquivos Static.md
├── Intro_Flask.md
├── Banco de Dados.md
├── Flask.md
├── Templates.md
└── Formulários Web.md
/imagens/CMD.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/the-akira/Flask-Programacao-Web/master/imagens/CMD.png
--------------------------------------------------------------------------------
/imagens/Avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/the-akira/Flask-Programacao-Web/master/imagens/Avatar.png
--------------------------------------------------------------------------------
/imagens/Tabelas.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/the-akira/Flask-Programacao-Web/master/imagens/Tabelas.png
--------------------------------------------------------------------------------
/imagens/FlaskLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/the-akira/Flask-Programacao-Web/master/imagens/FlaskLogo.png
--------------------------------------------------------------------------------
/imagens/HTMLForm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/the-akira/Flask-Programacao-Web/master/imagens/HTMLForm.png
--------------------------------------------------------------------------------
/imagens/FlaskTemplate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/the-akira/Flask-Programacao-Web/master/imagens/FlaskTemplate.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Gabriel Felippe
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
Programação Web com Flask
2 |
3 |
4 |
5 |
6 |
7 |
8 | Este guia tem como objetivo ensinar Programação Web com Python através do microframework Flask
9 |
10 |
11 | ### Conteúdo
12 |
13 | 01. [Introdução](https://github.com/the-akira/Flask-Programacao-Web/blob/master/capitulos/Flask.md)
14 | 02. [Templates](https://github.com/the-akira/Flask-Programacao-Web/blob/master/capitulos/Templates.md)
15 | 03. [Arquivos Static](https://github.com/the-akira/Flask-Programacao-Web/blob/master/capitulos/Arquivos%20Static.md)
16 | 04. [Formulários Web](https://github.com/the-akira/Flask-Programacao-Web/blob/master/capitulos/Formul%C3%A1rios%20Web.md)
17 | 05. [Banco de Dados](https://github.com/the-akira/Flask-Programacao-Web/blob/master/capitulos/Banco%20de%20Dados.md)
18 | 06. [Guia Rápido](https://github.com/the-akira/Flask-Programacao-Web/blob/master/capitulos/Intro_Flask.md)
19 | 07. [Referências](https://github.com/the-akira/Flask-Programacao-Web/blob/master/capitulos/Refer%C3%AAncias.md)
--------------------------------------------------------------------------------
/capitulos/Referências.md:
--------------------------------------------------------------------------------
1 | # Referências
2 |
3 | - [Flask](https://flask.palletsprojects.com/en/1.1.x/) | EN
4 | - [Make a Web App Using Python & Flask](https://aryaboudaie.com/python/technical/educational/web/flask/2018/10/17/flask.html) | EN
5 | - [Flask SQLAlchemy Tutorial](https://kite.com/blog/python/flask-sqlalchemy-tutorial/) | EN
6 | - [Flask (Wikipedia)](https://en.wikipedia.org/wiki/Flask_(web_framework)) | EN
7 | - [Flask Tutorial](http://www.patricksoftwareblog.com/flask-tutorial/) | EN
8 | - [Explore Flask](https://exploreflask.com/en/latest/index.html) | EN
9 | - [Flask 101](http://www.blog.pythonlibrary.org/tag/flask/) | EN
10 | - [Full Stack Python](https://www.fullstackpython.com/flask.html) | EN
11 | - [Real Python](https://realpython.com/tutorials/flask/) | EN
12 | - [The Flask Mega-Tutorial](https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world) | EN
13 | - [Websites with Python Flask](https://opentechschool.github.io/python-flask/) | EN
14 | - [SQLAlchemy + Flask Tutorial](https://docs.graphene-python.org/projects/sqlalchemy/en/latest/tutorial/) | EN
15 | - [How to Flask](http://www.cs.fsu.edu/~jayarama/pythonsu18/Slides/Flask.pdf) | EN
16 | - [Flask Reddit Community](https://www.reddit.com/r/flask/) | EN
17 | - [Building Python-based, database-driven web applications](http://jonathansoma.com/tutorials/flask-sqlalchemy-mapbox/index.html) | EN
18 | - [Developing Web Applications with Flask](https://www.ntu.edu.sg/home/ehchua/programming/webprogramming/Python3_Flask.html) | EN
19 | - [Learn Flask for Python](https://www.youtube.com/watch?v=Z1RJmh_OqeA) | EN
20 | - [Flask Tutorials](https://www.youtube.com/watch?v=MwZwr5Tvyxo&list=PL-osiE80TeTs4UjLw5MM6OjgkjFeUxCYH) | EN
21 | - [REST API with Flask & SQLAlchemy](https://www.youtube.com/watch?v=PTZiDnuC86g) | EN
22 | - [Learning Flask](https://www.youtube.com/watch?v=BUmUV8YOzgM&list=PLF2JzgCW6-YY_TZCmBrbOpgx5pSNBD0_L) | EN
23 | - [Python Flask from Scratch](https://www.youtube.com/watch?v=zRwy8gtgJ1A&list=PLillGF-RfqbbbPz6GSEM9hLQObuQjNoj_) | EN
24 | - [Web Programming with Flask](https://www.youtube.com/watch?v=zdgYw-3tzfI) | EN
25 | - [Python Web Apps with Flask](https://www.youtube.com/watch?v=qla-KaMF-2Q) | EN
26 | - [Flask for Beginners Tutorial](https://www.youtube.com/watch?v=EnJKHVEzHFw) | EN
27 | - [Flask Tutorials](https://www.youtube.com/playlist?list=PLzMcBGfZo4-n4vJJybUVV3Un_NFS5EOgX) | EN
--------------------------------------------------------------------------------
/capitulos/Arquivos Static.md:
--------------------------------------------------------------------------------
1 | # Arquivos Static
2 |
3 | ## Introdução
4 |
5 | Os **Arquivos Static**, como seus nomes sugerem, são arquivos que não mudam, **estáticos**. Aplicações Web geralmente precisam servir esses arquivos, que seriam **imagens**, arquivos **Javascript** e **CSS**.
6 |
7 | Para trabalharmos com esses arquivos precisaremos criar um diretório chamado `static` dentro de nosso diretório principal que chamamos de `Exemplos`. Dentro do diretório `static` que criamos, vamos criar mais três diretórios respectivamente `css`, `js` e `imagens`.
8 |
9 | ### CSS
10 |
11 | No diretório `css` vamos criar um arquivo `style.css` com o seguinte conteúdo:
12 |
13 | ```css
14 | body {
15 | background-color: gray;
16 | color: white;
17 | }
18 | ```
19 |
20 | Veja que alteramos o fundo de Aplicação para a cor cinza e também alteramos a cor das fontes para branco, um estilo simples para testarmos.
21 |
22 | ### Javascript
23 |
24 | Agora vamos criar dentro do diretório `js` um arquivo chamado `main.js` com o seguinte conteúdo:
25 |
26 | ```javascript
27 | alert('Hello World')
28 | ```
29 |
30 | Veja que é apenas um simples **script** que exibe um alerta 'Hello World'.
31 |
32 | ### Imagens
33 |
34 | Agora vamos obter o logo do Flask:
35 |
36 | 
37 |
38 | Vamos salvá-lo em nosso diretório `imagens`.
39 |
40 | ### Estrutura
41 |
42 | A estrutura de nosso diretório static ficou dessa forma:
43 |
44 | ```
45 | static
46 | ├── css
47 | │ └── style.css
48 | ├── imagens
49 | │ └── flask-logo.png
50 | └── js
51 | └── main.js
52 | ```
53 |
54 | ## Servindo os Arquivos Static
55 |
56 | Agora em nossa aplicação, vamos editar o arquivo `app.py` de forma que possamos servir esses **arquivos static**, para isso vamos criar uma nova rota de exemplo.
57 |
58 | ```python
59 | @app.route('/static')
60 | def arquivos_static():
61 | return render_template('static.html')
62 | ```
63 |
64 | Veja que é apenas uma simples rota para teste, agora vamos criar um arquivo `static.html` dentro do diretório `templates` para que possamos servir o conteúdo estático. Nosso arquivo `static.html` contará com o seguinte conteúdo:
65 |
66 | ```html
67 | {% extends 'layout.html' %}
68 |
69 | {% block conteudo %}
70 |
71 |
Arquivos Static
72 |
73 |
74 | {% endblock %}
75 | ```
76 |
77 | Veja que estamos utilizando o conceito de Herança, em outras palavras, estamos herdando as propriedades de nosso layout principal e nosso conteúdo está contido dentro do `{% block conteudo %}`.
78 |
79 | Através da função **url_for()** estamos buscando dentro do diretório **static** nossos arquivos. Primeiramente carregamos o **CSS** que se encontra em `css/style.css`, depois carregamos nossa imagem que está contida em `imagens/flask.png` e por fim nosso script que se encontra em `js/main.js`.
80 |
81 | Agora podemos executar nossa aplicação através do comando `python app.py` e navegar até `http://127.0.0.1:5000/static` para vermos as alterações em nossa aplicação.
82 |
83 | Caso deseje experimentar com o código em tempo real: https://repl.it/@theakira/Flask-Static
84 |
85 | ## Sumarizando
86 |
87 | Nesse capítulo aprendemos como gerenciar e servir nossos arquivos static, podemos também observar que Flask torna muito fácil trabalharmos com esses arquivos.
88 |
89 | Caso você queira optar por utilizar uma **[CDN](https://www.webopedia.com/TERM/C/CDN.html)** para servir os seus arquivos static de uma forma mais eficiente, você pode checar este interessante **[Tutorial](https://www.digitalocean.com/community/tutorials/using-a-cdn-to-speed-up-static-content-delivery)**
90 |
91 | Agora que já temos o domínio dos Arquivos Static podemos começar a utilizar Formulários para capturar dados dos usuários de nossa aplicação.
--------------------------------------------------------------------------------
/capitulos/Intro_Flask.md:
--------------------------------------------------------------------------------
1 | # Flask Referência
2 |
3 | Flask é um **microframework** [WSGI](https://wsgi.readthedocs.io/) para desenvolvimento de aplicações web. É projetado para você começar desenvolvendo rápido e com facilidade. Com a possibilidade de escalar em aplicações complexas, sua história começou como um simples *wrapper* sob [Werkzeug](https://palletsprojects.com/p/werkzeug) e [Jinja](https://palletsprojects.com/p/jinja) e se tornou um dos frameworks Python de aplicações web mais popular.
4 |
5 | Flask oferece sugestões, porém não força nenhuma dependência ou *layout* de projeto, em outras palavras, te dá liberdade total. É importante lembrar que existem muitas extensões disponíveis para Flask, que tornam a possibilidade de adicionar novas funcionalidades em nossa aplicação muito mais fácil.
6 |
7 | - Website Oficial: [Flask](https://palletsprojects.com/p/flask/)
8 | - Documentação: [Flask Documentação](https://flask.palletsprojects.com/en/1.1.x/)
9 |
10 | ## Instalando Flask
11 |
12 | ```python
13 | pip install Flask
14 | ```
15 |
16 | ## Aplicação Simples
17 |
18 | ```python
19 | from flask import Flask
20 | app = Flask(__name__)
21 |
22 | @app.route('/')
23 | def hello_world():
24 | return 'Hello World'
25 | ```
26 |
27 | ## Rotas
28 |
29 | ```python
30 | @app.route('/teste/') # exemplo.com/teste/Gabriel
31 | def teste(nome):
32 | return 'Hello ' + nome # nos retornará Hello Gabriel
33 | ```
34 |
35 | ## Métodos Request Permitidos
36 |
37 | ```python
38 | @app.route('/teste') # Padrão, apenas permite requisições GET
39 | @app.route('/teste', methods=['GET', 'POST']) # Permite apenas os métodos GET e POST
40 | @app.route('/teste', methods=['PUT']) # Permite apenas o método PUT
41 | ```
42 |
43 | ## Configuração
44 |
45 | ```python
46 | app.config['CONFIG_NAME'] = 'valor' # Acesso direto à configuração
47 | app.config.from_envar('NOME_ENV_VAR') # Importando de uma variável de ambiente exportada com o caminho para o arquivo
48 | ```
49 |
50 | ## Templates
51 |
52 | ```python
53 | from flask import render_template
54 |
55 | @app.route('/')
56 | def index():
57 | return render_template('index.html', variavel=variavel)
58 | ```
59 |
60 | ## Respostas em JSON
61 |
62 | ```python
63 | import jsonify
64 |
65 | @app.route('/arquivojson')
66 | def arquivojson():
67 | lista = [1,2,3,4,5]
68 | dict = {'números': lista, 'nome': 'Números'}
69 |
70 | return jsonify({'output': dict})
71 | ```
72 |
73 | ## Acessando Dados de Request
74 |
75 | ```python
76 | request.args['nome'] # acessando argumentos query
77 | request.form['nome'] # acessando dados de formulário
78 | request.method # tipo de requisição
79 | request.cookies.get('nome_do_cookie') # acessa dados de cookies
80 | request.session['nome'] # acessa dados sobre a sessão
81 | request.files['nome'] # acessa dados de arquivos
82 | ```
83 |
84 | ## Redirecionando
85 |
86 | ```python
87 | from flask import url_for, redirect
88 |
89 | @app.route('/home')
90 | def home():
91 | return render_template('home.html')
92 |
93 | @app.route('/redirecionar')
94 | def redirecionar():
95 | return redirect(url_for('home'))
96 | ```
97 |
98 | ## Abortando
99 |
100 | ```python
101 | from flask import abort
102 |
103 | @app.route('/')
104 | def index():
105 | abort(404) # Retorna o erro 404
106 | render_template('index.html') # Essa linha nunca é executada
107 | ```
108 |
109 | ## Setando Cookies
110 |
111 | ```python
112 | from flask import make_response
113 |
114 | @app.route('/')
115 | def index():
116 | resposta = make_response(render_template('index.html'))
117 | resposta.set_cookie('nome_do_cookie', 'valor_do_cookie')
118 | return resposta
119 | ```
120 |
121 | ## Lidando com Sessões
122 |
123 | ```python
124 | import session
125 |
126 | app.config['SECRET_KEY'] = 'qualquer string aleatória'
127 |
128 | @app.route('/login_sucesso') # Seta a sessão
129 | def login_sucesso():
130 | session['nome_da_chave'] = 'valor_da_chave' # Guarda um cookie seguro no browser
131 | return redirect(url_for('index'))
132 |
133 | @app.route('/') # Sessão de leitura
134 | def index():
135 | if 'nome_da_chave' in session:
136 | # Sessão existe e há uma chave
137 | session_var = session['valor_da_chave']
138 | else:
139 | # Sessão não existe
140 | ```
141 |
142 | ## Estruturando uma Aplicação com Blueprints
143 |
144 | A árvore seguinte representa um projeto com o nome de **projeto**
145 |
146 | ```
147 | run.py
148 | projeto/
149 | __init__.py
150 | config.py
151 | forms.py
152 | models.py
153 | admin/
154 | __init__.py
155 | routes.py
156 | main/
157 | __init__.py
158 | routes.py
159 | templates/
160 | index.html
161 | static/
162 | css/
163 | style.css
164 | ```
165 |
166 | ### Em `run.py`
167 |
168 | ```python
169 | from projeto import app
170 |
171 | if __name__ == '__main__':
172 | app.run()
173 | ```
174 |
175 | ### Em `projeto/__init__.py`
176 |
177 | ```python
178 | from flask import Flask
179 | from project.main.routes import main
180 | from project.admin.routes import admin
181 |
182 | app = Flask(__name__)
183 |
184 | app.register_blueprint(main, url_prefix='/')
185 | app.register_blueprint(admin, url_prefix='/admin')
186 | ```
187 |
188 | ### Em `projeto/main/routes.py`
189 |
190 | ```python
191 | from flask import Blueprint
192 |
193 | main = Blueprint('main', __name__)
194 |
195 | @main.route('/')
196 | def index():
197 | return "Hello, World! Essa é a página principal."
198 | ```
199 |
200 | ### Em `projeto/admin/routes.py`
201 |
202 | ```python
203 | from flask import Blueprint
204 |
205 | admin = Blueprint('admin', __name__)
206 |
207 | @main.route('/')
208 | def index():
209 | return "Hello, World! Essa é a página de administração."
210 | ```
211 |
212 | ## Utilizando Jinja2 (Template Engine)
213 |
214 | Renderizando um template através de `main/routes.py`:
215 |
216 | ```python
217 | from flask import Blueprint, render_template
218 |
219 | @main.route('/')
220 | def home():
221 | # Define uma lista de dicionários posts
222 | posts = [
223 | {
224 | "texto": "Hello, this is a post",
225 | "data": "This is a date and time",
226 | }
227 | ]
228 |
229 | # Passa a lista de dicionários para o template HTML
230 | return render_template("home.html", posts=posts)
231 | ```
232 |
233 | Utilizando Jinja2 dentro de `templates/home.html`:
234 |
235 | ```html
236 | {% for posts in posts %}
237 |
259 | {% endblock %}
260 | ```
261 |
262 | Em `base.html` vamos definir nossa estrutura base:
263 |
264 | ```html
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 | {% blockcontent %}{% endblock %}
273 |
274 |
275 | ```
276 |
277 | Utilizando **include** para adicionar arquivos parciais:
278 |
279 | ```
280 | {% include '_post.html' %}
281 | ```
282 |
283 | Adicionando **folhas de estilo** para `templates/base.html`:
284 |
285 | ```html
286 |
287 | ```
288 |
289 | ## Criando Modelos com SQLAlchemy
290 |
291 | Em `projeto/models.py`:
292 |
293 | ```python
294 | from flask_sqlalchemy import SQLAlchemy
295 |
296 | db = SQLAlchemy()
297 |
298 | class Post(db.Model):
299 | id = db.Column(db.Integer, primary_key=True)
300 | body = db.Column(db.String(256))
301 |
302 | def __init__(self, body):
303 | self.body = body
304 |
305 | def __repr__(self):
306 | return "".format(self.id)
307 | ```
308 |
309 | Em `projeto/__init__.py`:
310 |
311 | ```python
312 | from projeto.models import db
313 |
314 | app = Flask(__name__)
315 | app.config.from_object(config.DevelopmentConfig)
316 | with app.app_context():
317 | db.init_app()
318 | ```
319 |
320 | Em `projeto/config.py`:
321 |
322 | ```python
323 | import os
324 | BASE_DIR = os.path.abspath(os.path.dirname(__name__))
325 |
326 | class BaseConfig(object):
327 | # ...
328 | SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(BASE_DIR, 'data.db')
329 | SQLALCHEMY_TRACK_MODIFICATIONS = False
330 | ```
331 |
332 | Criando a Base de Dados e interagindo com os modelos e *queries* dentro de uma [shell](https://en.wikipedia.org/wiki/Shell_(computing))
333 |
334 | ```
335 | >>> from projeto import app, db
336 | >>> from projeto.models import Post
337 | >>> app.app_context().push()
338 | >>> db.create_all()
339 | >>> p = Post('Hello!')
340 | >>> db.session.add(p)
341 | >>> db.session.commit()
342 | >>> posts = Post.query.all()
343 | ```
344 |
345 | ## Plugins Importantes
346 |
347 | - [Flask-PyMongo](https://flask-pymongo.readthedocs.io/en/latest/)
348 | - [Flask-SQLAlchemy](https://pypi.org/project/Flask-SQLAlchemy/)
349 | - [Flask-Mail](https://pythonhosted.org/Flask-Mail/)
350 | - [Flask-RESTful](https://flask-restful.readthedocs.io/en/latest/)
351 | - [Flask-Uploads](https://flask-uploads.readthedocs.io/en/latest/)
352 | - [Flask-WTF](https://flask-wtf.readthedocs.io/en/stable/)
353 | - [Flask-User](https://flask-user.readthedocs.io/en/latest/)
354 | - [Flask-Login](https://flask-login.readthedocs.io/en/latest/)
355 |
--------------------------------------------------------------------------------
/capitulos/Banco de Dados.md:
--------------------------------------------------------------------------------
1 | # Banco de Dados
2 |
3 | ## Introdução
4 |
5 | Um **[banco de dados](https://pt.wikipedia.org/wiki/Banco_de_dados)** é uma coleção organizada de dados, geralmente armazenados e acessados eletronicamente através de um sistema de computador.
6 |
7 | O **[sistema de gerenciamento de banco de dados (DBMS)](https://pt.wikipedia.org/wiki/Sistema_de_gerenciamento_de_banco_de_dados)** é o software que interage com usuários finais, aplicativos e o próprio banco de dados para capturar e analisar os dados.
8 |
9 | **[Conolly e Begg](https://www.pearson.com/us/higher-education/program/Connolly-Database-Systems-A-Practical-Approach-to-Design-Implementation-and-Management-6th-Edition/PGM116956.html)** definem os Sistemas de Gerenciamento de Banco de Dados como um software que habilita usuários a definir, criar, manter e controlar o acesso ao banco de dados.
10 |
11 | A funcionalidade provida por uma DBMS pode variar bastante entre cada solução, porém as principais funcionalidades são o armazenamento, recuperação e atualização dos dados.
12 |
13 | Em determinado momento muitos aplicações web terão de lidar com banco de dados, como já estudamos, por ser um microframework, Flask não traz consigo um mecanismo para lidarmos com banco de dados, porém existe uma solução muito eficaz chamada **[SQLAlchemy](https://www.sqlalchemy.org/)**, que é um kit de ferramentas SQL para a linguagem Python e um **[Object Relational Mapper](https://en.wikipedia.org/wiki/Object-relational_mapping)** que fornece aos desenvolvedores de aplicação o poder total e flexibilidade de SQL.
14 |
15 | Um Object Relational Mapper é basicamente uma camada de abstração que se estabelece no topo de **[consultas SQL brutas](https://www.codecademy.com/articles/sql-commands)** sendo executadas em nosso banco dados. SQLAlchemy nos fornece uma **[API](https://en.wikipedia.org/wiki/Application_programming_interface)** consistente para diversos sistemas de banco de dados, os mais populares incluem MySQL, PostgreSQL e SQLite e também torna fácil movermos dados entre nossos modelos e nosso banco de dados e ainda torna fácil por exemplo a alteração de um sistema de banco de dados e a migração de nossos **[schemas](https://en.wikipedia.org/wiki/Database_schema)**. Em resumo, SQLAlchemy nos possibilitará escrever todo o código em Python, sem a necessidade de escrevermos códigos SQL.
16 |
17 | Neste guia específico, vamos utilizar a versão do SQLAlchemy para Flask chamada de **[Flask-SQLAlchemy](https://flask-sqlalchemy.palletsprojects.com/en/2.x/)** que nos possibilitará um início rápido e vamos escolher o sistema gerenciador de banco de dados [SQLite3](https://www.sqlite.org/index.html), que embora não seja uma solução escalável, ela é muito conveniente para pequenas aplicações como a nossa, além disso, em SQLite cada base de dados é armazenada em um único arquivo em disco e não há necessidade de executarmos um servidor de banco de dados como em MySQL e PostgreSQL.
18 |
19 | ## Flask-SQLAlchemy
20 |
21 | Flask-SQLAlchemy torna ainda mais simples o uso da biblioteca SQLAlchemy com Flask, para trabalharmos com ela é necessário fazermos sua instalação, com nosso Ambiente Virtual ativo, vamos digitar:
22 |
23 | ```
24 | pip install flask-sqlalchemy
25 | ```
26 |
27 | Uma vez instalada com sucesso, agora temos a biblioteca a nossa disposição para trabalharmos. Vamos então criar um novo arquivo chamado `models.py` dentro da pasta `Exemplos` este arquivo que representará um modelo de uma simples biblioteca.
28 |
29 | Vamos começar com as configurações básicas
30 |
31 | ```python
32 | from flask import Flask
33 | from flask_sqlalchemy import SQLAlchemy
34 |
35 | app = Flask(__name__)
36 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///livros.db'
37 | db = SQLAlchemy(app)
38 | ```
39 |
40 | Nós primeiramente importamos as bibliotecas necessárias, que nesse caso é o próprio Flask e o Flask-SQLAlchemy, posteriormente criamos nosso objeto **app** e indicamos onde o arquivo do banco de dados deve ficar localizado (root da aplicação). Por fim criamos o objeto **db** que nos permite integrar SQLAlchemy na nossa aplicação Flask.
41 |
42 | Agora que temos nossas configurações estabelecidades, vamos começar a trabalhar efetivamente em nosso banco de dados, vamos criar uma classe **Autor** que representará uma tabela Autor em nosso banco de dados, este será nosso modelo.
43 |
44 | ```python
45 | class Autor(db.Model):
46 | id = db.Column(db.Integer, primary_key=True)
47 | nome = db.Column(db.String(30), nullable=False)
48 | sobrenome = db.Column(db.String(30), nullable=False)
49 | livros = db.relationship('Livro', backref='autor', lazy=True)
50 |
51 | def __repr__(self):
52 | return f'Autor("{self.nome}", "{self.sobrenome}")'
53 | ```
54 |
55 | Começamos definindo o nome de nossa classe como **Autor**, esta que herda de `db.Model`, uma classe base para todos os modelos de Flask-SQLAlchemy. Esta classe define diversos campos como variáveis de classe. Campos são criados como instâncias da classe `db.Column`, no qual recebem o campo como argumento, mais alguns argumentos adicionais como por exemplo `primary_key` que índica o campo como uma chave primária da tabela, que deve ser único.
56 |
57 | Nossa tabela então é composta pelos seguintes campos:
58 |
59 | - **id**, valor inteiro único que representa nossa chave primária
60 | - **nome**, String de até 30 caracteres que não pode ser nulo
61 | - **sobrenome**, String de até 30 caracteres que não pode ser nulo
62 | - **livros**, Este campo é um relacionamento com a classe **Livro** que vamos criar, o argumento **backref** define o nome do campo que será adicionado aos objetos da classe 'muitos' que apontará de volta para o objeto 'único'.
63 |
64 | Para entendermos melhor, estamos fazendo uma relação em que um **Autor** poderá ter a autoria sob muitos livros, porém um livro pertencerá a um único autor.
65 |
66 | É o que chamamos de relação **[one-to-many](https://en.wikipedia.org/wiki/One-to-many_(data_model))**
67 |
68 | - Por fim, o método mágico `__repr__` diz ao Python como imprimir os objetos dessa classe, o que será muito útil caso precisemos debuggar.
69 |
70 | Vamos agora criar a Classe que representará a tabela **Livros**
71 |
72 | ```python
73 | class Livro(db.Model):
74 | id = db.Column(db.Integer, primary_key=True)
75 | titulo = db.Column(db.String(100), nullable=False)
76 | sumario = db.Column(db.String(250), nullable=False)
77 | autor_id = db.Column(db.Integer, db.ForeignKey('autor.id'), nullable=False)
78 |
79 | def __repr__(self):
80 | return f'Livro("{self.titulo}")'
81 | ```
82 |
83 | Como em nossa classe Autor, começamos definindo o nome de nossa classe como **Livro** e herdamos de `db.Model`. Criamos o campo **id** que representa nossa chave primária, o **titulo** de nosso livro que será uma String de até 100 caracteres e o **sumario** que será uma string de até 250 caracteres. O campo **autor_id** é chamado de **ForeignKey**, que seria como uma chave estrangeira para acessarmos o autor do respectivo livro.
84 |
85 | - Por fim, o método mágico `__repr__` diz ao Python como imprimir os objetos dessa classe.
86 |
87 | A imagem a seguir ilustra o [esquema](https://en.wikipedia.org/wiki/Database_schema) de nosso banco de dados:
88 |
89 | 
90 |
91 | Agora que nossos modelos estão estabelecidos, é hora de testarmos nosso banco de dados para termos certeza que podemos inserir dados. Dentro de seu diretório `Exemplos` onde reside o arquivo `models.py`, abra o interpretador Python para que possamos trabalhar interactivamente. Para isso digite `python`
92 |
93 | Começamos com os imports necessários
94 |
95 | ```python
96 | >>> from models import db, Autor, Livro
97 | ```
98 |
99 | Inicializando Banco de Dados e Tabelas
100 |
101 | ```python
102 | >>> db.create_all()
103 | ```
104 |
105 | Cadastrando um autor em nosso banco de dados
106 |
107 | ```python
108 | >>> autor = Autor(nome='Aldous', sobrenome='Huxley')
109 | >>> db.session.add(autor)
110 | >>> db.session.commit()
111 | ```
112 |
113 | Perceba que é necessário primeiro adicionarmos o **autor** criado através de `db.session.add()` e depois para escrevermos as mudanças em nosso banco de dados executamos `db.session.commit()`. Feito, conseguimos popular a nossa tabela autor com nosso primeiro autor! Vamos agora cadastrar um livro
114 |
115 | ```python
116 | >>> livro = Livro(titulo='Brave New World', sumario='Dystopian Novel', autor_id=autor.id)
117 | >>> db.session.add(livro)
118 | >>> db.session.commit()
119 | ```
120 |
121 | Seguimos o mesmo procedimento anterior, exceto que passamos o atributo **id** do autor para o campo **autor_id**, dessa forma estamos cadastrando o autor que criamos recentemente como o autor deste livro. Agora que temos os cadastro feitos, podemos nos referir aos campos de nossas tabelas para acessarmos os dados, por exemplo
122 |
123 | Se desejarmos acessar os atributos da tabela Livro
124 |
125 | ```python
126 | >>> livro.titulo
127 | >>> livro.sumario
128 | >>> livro.autor
129 | ```
130 |
131 | Nos retornará, respectivamente
132 |
133 | ```
134 | 'Brave New World'
135 | 'Dystopian Novel'
136 | Autor("Aldous", "Huxley")
137 | ```
138 |
139 | Podemos fazer o mesmo com a tabela Autor
140 |
141 | ```python
142 | >>> autor.nome
143 | >>> autor.sobrenome
144 | >>> autor.livros
145 | ```
146 |
147 | Que nos retorna
148 |
149 | ```
150 | 'Aldous'
151 | 'Huxley'
152 | [Livro("Brave New World")]
153 | ```
154 |
155 | Podemos também executar consultas através de SQLAlchemy, vejamos alguns exemplos
156 |
157 | ```python
158 | >>> Autor.query.all()
159 | ```
160 |
161 | O método `query.all()` nos retorna todos os Autores em nosso banco de dados, nesse caso só temos um, então nos será retornado `[Autor("Aldous", "Huxley")]`.
162 |
163 | Podemos também consultar um livro pelo seu respectivo **id**:
164 |
165 | ```python
166 | Livro.query.get(1)
167 | ```
168 |
169 | O método `query.get()` recebe como parâmetro um **id** e nos retorna o respectivo Livro com tal **id**, nesse caso: `Livro("Brave New World")`
170 |
171 | ## Sumarizando
172 |
173 | - Neste capítulo estudamos os conceitos básicos de banco de dados
174 | - Vimos alguns Sistemas Gerenciadores de Banco de Dados e sua importância
175 | - Aprendemos sobre o conceito de **ORM**
176 | - Utilizamos o **ORM** Flask-SQLAlchemy para criarmos nosso primeiro banco de dados
177 | - Executamos algumas manipulações básicas como inserção de dados e consultas simples
178 |
179 | Este Capítulo fecha a saga dos **fundamentos básicos de Flask**, agora vamos partir para a segunda parte de nossa caminhada, onde vamos aprender a estruturar uma aplicação de uma forma mais consistente, bem como unir todos os conhecimentos que adquirimos até então.
--------------------------------------------------------------------------------
/capitulos/Flask.md:
--------------------------------------------------------------------------------
1 | # Flask
2 |
3 | Este projeto tem como objetivo apresentar o microframework Flask e suas principais características que nos possibilitam desenvolver aplicações web completas e **[escaláveis](https://www.romexsoft.com/blog/scalable-website/)**.
4 |
5 | ## Introdução
6 |
7 | **[Flask](https://flask.palletsprojects.com/en/1.1.x/)** foi criado por Armin Ronacher, seu lançamento oficial ocorreu no ano de 2010. Por ser pequeno, leve e minimalista, ele é conhecido como um microframework Python, mas isso não quer dizer que ele seja somente limitado a aplicações pequenas, muito pelo contrário, Flask pode ser utilizado em grandes aplicações que escalam, sua fama de microframework ocorre pelo fato de Flask manter o núcleo de uma aplicação simples, porém extensível, Flask não tem construído uma camada de abstração para banco de dados, nem mesmo validação para formulários, porém Flask suporta **extensões** que são capazes de adicionar todas essas funcionalidades para nossa aplicação.
8 |
9 | Flask na verdade é a união de dois frameworks muito populares.
10 |
11 | 1. **[Werkzeug](https://palletsprojects.com/p/werkzeug/)**: Uma biblioteca **[WSGI (Web Server Gateway Interface)](https://en.wikipedia.org/wiki/Web_Server_Gateway_Interface)** para Python, que inclui sistema de rotas de URL, objetos *request* e *response* com características completas e um poderoso *debugger*.
12 | 2. **[Jinja2](https://palletsprojects.com/p/jinja/)**: Um **[Template Engine](https://en.wikipedia.org/wiki/Template_processor)** completo para Python
13 |
14 | Como já vimos, tarefas de alto-nível como acesso ao banco de dados, formulários web e autenticação de usuários são suportados através de **'extensões'**.
15 |
16 | Dentro do escopo da arquitetura **[MVC (Model-View-Controller)](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)**, Werkzeug abrange o Controller (C) e Jinja2 abrange a View (V). Flask não fornece um Model (M) integrado, ele deixa você escolher sua solução de banco de dados favorita. Uma escolha muito popular é **[Flask-SQLAlchemy](https://flask-sqlalchemy.palletsprojects.com/en/2.x/)** que traz um **[ORM (Object-Relational Mapper)](https://en.wikipedia.org/wiki/Object-relational_mapping)** sob um banco de dados relacional como MySQL ou PostgreSQL.
17 |
18 | Sumarizando, o microframework Flask é capaz de nos fornecer:
19 |
20 | - Uma interface em conformidade com o **WSGI**
21 | - Sistema de Rotas de URL e Request/Response
22 | - Cookies e Sessions Seguros
23 | - Servidor Web de desenvolvimento e Debugger construídos
24 | - Cliente de Unit Test para testes unitários
25 | - Templates Jinja2 (tags, filtros, macros, etc)
26 | - Extensiva Documentação
27 | - Extensões disponíveis para aperfeiçoar e agilizar o desenvolvimento
28 |
29 | Através de Flask você pode lidar com requisições HTTP e AJAX, rotear requests para um controller, avaliar e validar dados requisitados e também fornecer respostas com **HTML** ou **JSON**.
30 |
31 | Você pode conferir projetos e corporações que usam Flask em: **[flask-powered](https://github.com/rochacbruno/flask-powered)**
32 |
33 | ## Instalando Flask
34 |
35 | Para instalarmos Flask é necessário que tenhamos Python em nosso computador, caso você ainda não o tenha, faça o download através do **[Website Oficial](https://www.python.org/downloads/)** e você estará pronto para começar.
36 |
37 | Agora abra a sua **[Interface de Linha de Comandos](https://en.wikipedia.org/wiki/Shell_(computing))**  e digite:
38 |
39 | ```
40 | python --version
41 | ```
42 |
43 | Nos será retornada a versão atual do Python em nosso computador, em nosso caso estamos lidando com a versão `3.7.1`. Agora que já temos o Python em nossa máquina, podemos utilizar o **[pip](https://pypi.org/project/pip/)** para instalarmos o Flask. **Pip** é um gerenciador de pacotes Python que nos permite instalar diversos módulos escritos por outros desenvolvedores, ele já vem automaticamente instalado com Python, porém caso você tenha algum problema, poderá obtê-lo em: **[pip](https://pypi.org/project/pip/)**.
44 |
45 | Antes de instalarmos Flask é importante termos em mente o conceito de Ambientes Virtuais, que nos permitem isolar os pacotes utilizados em nosso projeto para que possamos evitar conflitos de versões com nosso ambiente global. A ferramenta **[virtualenv](https://virtualenv.pypa.io/en/latest/)** nos auxilia nesse processo, para obtermos ela é muito simples:
46 |
47 | ```
48 | pip install virtualenv
49 | ```
50 |
51 | Tendo a ferramenta **virtualenv** em nossa máquina, podemos agora criar um Ambiente Virtual, crie um diretório com o nome de sua escolha, em nosso caso será `Exemplo` e **dentro** dele digite o seguinte comando
52 |
53 | ```
54 | python -m venv ambiente
55 | ```
56 |
57 | Será criado para nós um diretório com o nome `ambiente`, este que representará o nosso ambiente virtual, para ativarmos ele digitamos o seguinte comando
58 |
59 | ```
60 | source ambiente/bin/activate
61 | ```
62 |
63 | ou
64 |
65 | ```
66 | . ambiente/bin/activate
67 | ```
68 |
69 | Para desativarmos basta digitarmos `deactivate` e o ambiente será desativado, por hora vamos mantê-lo ativo, pois iremos instalar a Biblioteca Flask através do **pip**:
70 |
71 | ```
72 | pip install flask
73 | ```
74 |
75 | Depois dessa rápida instalação, agora já temos Flask disponível para trabalharmos em nossa máquina. Antes de iniciarmos nossos experimentos com Flask, vamos experimentar alguns comandos **pip** importantes
76 |
77 | Listando Pacotes Instalados no Ambiente Virtual
78 |
79 | ```
80 | pip list
81 | ```
82 |
83 | Nos será trazida a seguinte resposta:
84 |
85 | ```
86 | Click 7.0
87 | Flask 1.1.1
88 | itsdangerous 1.1.0
89 | Jinja2 2.10.1
90 | MarkupSafe 1.1.1
91 | pip 10.0.1
92 | setuptools 39.0.1
93 | Werkzeug 0.15.5
94 | ```
95 |
96 | Criando um arquivo `requirements.txt`
97 |
98 | Através do **pip** é possível criarmos um arquivo `requirements.txt` que servirá como o guardião dos pacotes necessários para que nossa aplicação funcione corretamente. Caso alguma pessoa queira utilizar nossa aplicação, ela poderá instalar os requerimentos através desse arquivo. Para criarmos ele devemos digitar o seguinte comando:
99 |
100 | ```
101 | pip freeze > requirements.txt
102 | ```
103 |
104 | Nos será criado um arquivo `requirements.txt` com o seguinte conteúdo
105 |
106 | ```
107 | Click==7.0
108 | Flask==1.1.1
109 | itsdangerous==1.1.0
110 | Jinja2==2.10.1
111 | MarkupSafe==1.1.1
112 | Werkzeug==0.15.5
113 | ```
114 |
115 | Para instalar os pacotes contidos no arquivo `requirements.txt` podemos utilizar o comando
116 |
117 | ```
118 | pip install -r requirements.txt
119 | ```
120 |
121 | Caso tenha interesse em se aprofundar mais na questão dos Ambientes Virtuais, você pode visitar os guias: **[Gerenciadores de Pacotes e Ambientes Virtuais](https://github.com/the-akira/Python-Iluminado/blob/master/Capitulos/29.PIP.md)** e **[Environment](https://exploreflask.com/en/latest/environment.html#)**
122 |
123 | ## Flask Hello World
124 |
125 | Agora que já temos nosso Ambiente Virtual funcionando corretamente e Flask está instalado em nosso Ambiente isolado, vamos criar nosso primeiro **Hello World**. Para isso, vamos criar em nosso diretório `Exemplo` o arquivo `app.py` que terá o seguinte conteúdo
126 |
127 | ```python
128 | from flask import Flask
129 | app = Flask(__name__)
130 |
131 | @app.route('/')
132 | def hello_world():
133 | return 'Hello World!'
134 |
135 | if __name__ == '__main__':
136 | app.run()
137 | ```
138 |
139 | 1. Começamos importando a classe Flask. Uma instância dessa classe será nossa aplicação **WSGI**
140 | 2. Em seguida nós criamos uma instância dessa classe. O primeiro argumento é o nome do módulo da aplicação ou pacote. Se você estiver utilizando um único módulo (como em nosso exemplo) você deve usar `__name__` porque dependendo se é iniciado como aplicativo ou importado como módulo, o nome será diferente, isso é necessário para que Flask saiba onde buscar Templates e arquivos Static.
141 | 3. Nós usamos o **[decorador](https://realpython.com/primer-on-python-decorators/)** **route()** para dizer ao Flask qual URL irá ativar nossa função.
142 | 4. À função é dado um nome no qual também é utilizado para gerar URL's para essa função em particular, e retorna a mensagem que desejamos apresentar no **[browser](https://en.wikipedia.org/wiki/Web_browser)** do usuário.
143 | 5. O bloco **if** é basicamente uma forma de Python dizer 'Execute esse código apenas se eu rodá-lo'. Como vimos antes `__name__` é uma variável que Python cria automaticamente e ela é igual a `__main__` quando somos nós que rodamos o código.
144 |
145 | Em resumo podemos dizer que:
146 |
147 | ```python
148 | # Importamos e criamos nosso app
149 | from flask import Flask
150 | app = Flask(__name__)
151 |
152 | # Especificamos nossa rota, sua função e seu retorno
153 | @app.route('/')
154 | def hello_world():
155 | return 'Hello World!'
156 |
157 | # Roda o nosso app
158 | if __name__ == '__main__':
159 | app.run()
160 | ```
161 |
162 | Agora finalmente podemos executar nossa aplicação através do comando `python app.py`. Por padrão Flask irá utilizar a porta 5000 de nosso localhost, então navegue até `http://127.0.0.1:5000/` para ver nossa aplicação funcionando. Perfeito, conseguimos executar nossa primeira aplicação em Flask, é incrível como em poucas linhas de códigos conseguimos obter este resultado. Flask é realmente poderoso.
163 |
164 | Caso queiramos disponibilizar nossa aplicação para outras pessoas em nossa rede, podemos passar o parâmetro **host** para a função **run()**, vejamos
165 |
166 | ```python
167 | if __name__ == '__main__':
168 | app.run(host='0.0.0.0')
169 | ```
170 |
171 | Para alterarmos a porta em que a aplicação é disponibilizada passamos o parâmetro **port** para a função **run()**
172 |
173 | ```python
174 | if __name__ == '__main__':
175 | app.run(port=3535)
176 | ```
177 |
178 | Nossa aplicação agora estará rodando na porta **3535**. Por fim, mas não menos importante, podemos ativar o modo **debug** para facilitar o processo de desenvolvimento de nossas aplicações.
179 |
180 | ```python
181 | if __name__ == '__main__':
182 | app.run(debug=True)
183 | ```
184 |
185 | Atribuindo **debug** como **True** nos permite um maior controle sobre os erros que ocorrem durante o desenvolvimento, porém é importante lembrarmos que devemos utilizar somente durante essa fase, jamais devemos utilizar em produção.
186 |
187 | Você pode ver o exemplo em tempo real acessando: https://repl.it/@theakira/Flask-Introducao
188 |
189 | ## Sumarizando
190 |
191 | Neste capítulo aprendemos
192 |
193 | - Um pouco sobre a história do microframework Flask
194 | - Instalação do Flask
195 | - Manipulando Ambientes Virtuais
196 | - Criação de uma simples aplicação `Hello World`
197 | - Conhecimento básico da estrutura do Flask
--------------------------------------------------------------------------------
/capitulos/Templates.md:
--------------------------------------------------------------------------------
1 | # Templates
2 |
3 | ## Introdução
4 |
5 | Templates são um elemento essencial em Flask e muitos outros frameworks, eles são o que chamamos de camada de apresentação ou *view* de nossa aplicação. Um template é um arquivo que contém textos estáticos, assim como *placeholders* para renderizarmos conteúdo dinâmico.
6 |
7 | 
8 |
9 | Como podemos ver na ilustração:
10 |
11 | - Temos nossos dados armazenados em um banco de dados
12 | - Temos um arquivo template
13 |
14 | O template engine conecta com o modelo de dados e processa os códigos específicos nos templates e então direciona o output para um arquivo específico que normalmente é renderizado em um browser de forma que possamos ver a apresentação dos dados.
15 |
16 | ## Jinja2
17 |
18 | Jinja é uma moderna e amigável linguagem de Templates para Python. É veloz, amplamente utilizado e seguro e conta com características como:
19 |
20 | - Execução em Sandbox
21 | - Um poderoso sistema automático de *escaping* para prevenção contra ataques **[XSS](https://en.wikipedia.org/wiki/Cross-site_scripting)**
22 | - Herança de Templates
23 | - Compila para código python otimizado *[just in time](https://en.wikipedia.org/wiki/Just-in-time_compilation)*
24 | - Fácil de debugar
25 | - Sintáxe Configurável
26 |
27 | Podemos instalar Jinja2 através do comando `pip install Jinja2`, porém uma vez que já instalamos Flask, não precisamos fazê-lo, pois Jinja2 já faz parte do Flask.
28 |
29 | É muito importante sabermos que existem dois tipos de delimitadores em Jinja2:
30 |
31 | - `{% ... %}`: Usado para executar comandos como **for loops** ou atribuição de valores
32 | - `{{ ... }}`: Usado para imprimirmos o resultado de expressões no template
33 |
34 | ## Trabalhando com Templates em Flask
35 |
36 | Atualmente contamos com um diretório `Exemplos` que contém nosso Ambiente Virtual, o arquivo `requirements.txt` e nossa simples aplicação `app.py` que no momento contém o seguinte código:
37 |
38 | ```python
39 | from flask import Flask
40 | app = Flask(__name__)
41 |
42 | @app.route('/')
43 | def hello_world():
44 | return 'Hello World!'
45 |
46 | if __name__ == '__main__':
47 | app.run(debug=True)
48 | ```
49 |
50 | Dentro de nosso diretório `Exemplos` vamos agora criar um novo diretório chamado de `templates` e nele vamos criar um arquivo `index.html` com o seguinte conteúdo:
51 |
52 | ```html
53 |
Nosso primeiro Template
54 | ```
55 |
56 | Agora vamos modificar nosso arquivo `app.py` de forma que possamos servir esse template.
57 |
58 | ```python
59 | from flask import Flask, render_template
60 | app = Flask(__name__)
61 |
62 | @app.route('/')
63 | def index():
64 | return render_template('index.html')
65 |
66 | if __name__ == '__main__':
67 | app.run(debug=True)
68 | ```
69 |
70 | Observe que tivemos que importar a função **render_template()** para podermos trabalhar com os templates.
71 |
72 | - Essa função recebe como argumento o **nome do template** a ser renderizado ou um iterável com nomes de templates, em que o primeiro será renderizado.
73 | - O **contexto**, em outras palavras: as variáveis que estarão disponíveis no contexto do template.
74 |
75 | Para saber mais detalhes sobre a função visite: **[flask.render_template](https://flask.palletsprojects.com/en/1.1.x/api/#flask.render_template)**
76 |
77 | Agora execute a aplicação com o comando `python app.py` e visite `http://127.0.0.1:5000/` para ver o resultado.
78 |
79 | ## Passando Variáveis para o Template
80 |
81 | Até então renderizamos um arquivo HTML estático, o que já nos fornece bastante flexibilidade para criarmos páginas web, porém o grande poder dos templates está em sua capacidade de receber variáveis para que possamos apresentar os dados para os usuários que irão visualizar nossa aplicação.
82 |
83 | Vamos alterar nossos arquivos `app.py` e `index.html` para vermos um simples exemplo
84 |
85 | ```python
86 | from flask import Flask, render_template
87 | app = Flask(__name__)
88 |
89 | @app.route('/')
90 | def index():
91 | nome = 'Gabriel'
92 | return render_template('index.html', nome=nome)
93 |
94 | if __name__ == '__main__':
95 | app.run(debug=True)
96 | ```
97 |
98 | ```html
99 |
Olá {{ nome }}
100 | ```
101 |
102 | Novamente executamos nossa aplicação através do comando `python app.py` e podemos ver o resultado que nos é retornado.
103 |
104 | ### Trabalhando com Estruturas de Dados, Funções e Classes
105 |
106 | Vamos agora criar uma nova rota e um novo template de forma que possamos trabalhar com estruturas variadas. Em nosso diretório **templates** vamos criar o arquivo `jinja.html`, no momento ele terá o seguinte conteúdo
107 |
108 | ```html
109 |
Jinja Template
110 | ```
111 |
112 | Agora vamos alterar nosso arquivo `app.py` de forma que possamos servir esse novo template
113 |
114 | ```python
115 | from flask import Flask, render_template
116 | app = Flask(__name__)
117 |
118 | @app.route('/')
119 | def index():
120 | nome = 'Gabriel'
121 | return render_template('index.html', nome=nome)
122 |
123 | @app.route('/jinja')
124 | def jinja():
125 | return render_template('jinja.html')
126 |
127 | if __name__ == '__main__':
128 | app.run(debug=True)
129 | ```
130 |
131 | Executamos nossa aplicação e agora podemos navegar até `http://127.0.0.1:5000/jinja` para ver o resultado. Agora vamos criar alguns dados para experimentarmos em nosso template, novamente vamos editar o arquivo `app.py` com o seguinte conteúdo:
132 |
133 | ```python
134 | from flask import Flask, render_template
135 | app = Flask(__name__)
136 |
137 | @app.route('/')
138 | def index():
139 | nome = 'Gabriel'
140 | return render_template('index.html', nome=nome)
141 |
142 | @app.route('/jinja')
143 | def jinja():
144 | num = 10
145 |
146 | langs = ['Python', 'Javascript', 'C', 'C++', 'Bash', 'Haskell']
147 |
148 | personagens = {
149 | 'DBZ': 'Goku',
150 | 'Naruto': 'Kakashi',
151 | 'Death Note': 'Raito Yagami',
152 | 'Akira': 'Tetsuo',
153 | 'Pokémon': 'Ash'
154 | }
155 |
156 | cores = ('red', 'green', 'blue')
157 |
158 | online = True
159 |
160 | class Aplicacao:
161 | def __init__(self, nome, descricao, url):
162 | self.nome = nome
163 | self.descricao = descricao
164 | self.url = url
165 |
166 | def nome(self):
167 | return f'O nome da aplicação é {self.nome}'
168 |
169 | def endereco(self):
170 | return f'Você pode acessar a aplicação atraves do endereço: {self.url}'
171 |
172 | app = Aplicacao(nome='Flask Programação Web', descricao='Tutorial Completo', url='https://github.com/the-akira')
173 |
174 | def cubo(x):
175 | return x**3
176 |
177 | html = "
Titulo Grande
"
178 |
179 | malicioso = ""
180 |
181 | return render_template(
182 | 'jinja.html',
183 | num=num,
184 | langs=langs,
185 | personagens=personagens,
186 | cores=cores,
187 | online=online,
188 | Aplicacao=Aplicacao,
189 | cubo=cubo,
190 | app=app,
191 | html=html,
192 | malicioso=malicioso
193 | )
194 |
195 | if __name__ == '__main__':
196 | app.run(debug=True)
197 | ```
198 |
199 | Podemos observar que:
200 |
201 | 1. Atribuimos o valor 10 à variável **num**
202 | 2. Criamos uma lista **langs** com importantes linguagens de programação
203 | 3. Criamos um dicionário **personagens** com personagens de animes
204 | 4. Criamos uma tupla **cores** com os valores **[RGB](https://www.w3schools.com/colors/colors_rgb.asp)**
205 | 5. Atribuimos o valor **True** à variável **online**
206 | 6. Definimos uma classe **Aplicacao** que modela uma Aplicação
207 | 7. Construimos um objeto **app** através do Construtor **Aplicacao()**
208 | 8. Definimos uma função **cubo()** que eleva um número passado por parâmetro ao cubo
209 | 9. Atribuimos uma String contendo HTML à variável **html**
210 | 10. Atribuimos um String contendo um Script à variável malicioso
211 | 11. Por fim, retornamos a função **render_template()** que recebe o nosso template `jinja.html` e todas as variáveis que definimos
212 |
213 | Agora que temos acesso a todos esses dados em nosso template, podemos editar nosso arquivo `jinja.html` para apresentarmos os dados em nossa página.
214 |
215 | ```html
216 |
Jinja Template
217 |
218 |
219 |
1. Variáveis
220 |
Número: {{ num }}
221 |
222 |
223 |
224 |
2. Percorrendo a lista com o For Loop
225 |
226 | {% for lang in langs %}
227 |
{{ lang }}
228 | {% endfor %}
229 |
230 |
231 |
232 |
233 |
3. Percorrendo a lista com índices através do For Loop
234 |
235 | {% for lang in langs %}
236 |
{{ loop.index }} : {{ lang }}
237 | {% endfor %}
238 |
239 |
240 |
241 |
242 |
4. Percorrendo dicionário com o For Loop
243 |
244 | {% for k, v in personagens.items() %}
245 |
{{ k }} : {{ v }}
246 | {% endfor %}
247 |
248 |
249 |
250 |
251 |
5. Acessando um Membro da Lista
252 |
{{ langs[0] }}
253 |
254 |
255 |
256 |
6. Acessando um Membro do Dicionário
257 |
{{ personagens['Akira'] }}
258 |
259 |
260 |
261 |
7. Acessando Atributos de Classe
262 |
{{ app.nome }}
263 |
264 |
265 |
266 |
8. Desempacotando Variáveis
267 |
{% set a, b, c = cores %}
268 |
{{ a }}
269 |
{{ b }}
270 |
{{ c }}
271 |
272 |
273 |
274 |
9. Chamando uma Função
275 |
{{ cubo(3) }}
276 |
{{ cubo(2) }}
277 |
278 |
279 |
280 |
10. Chamando um Método de Classe
281 |
{{ app.endereco() }}
282 |
283 |
284 |
285 |
11. Condicionais
286 | {% if online %}
287 |
Usuário se encontra online
288 | {% endif %}
289 |
290 |
291 |
292 |
12. Comparações
293 | {% if num < 18 %}
294 |
Valor menor que 18
295 | {% elif num == 20 %}
296 |
Valor é igual a 20
297 | {% else %}
298 |
Nenhuma das outras opções
299 | {% endif %}
300 |
301 |
302 |
303 |
13. Expressões Matemáticas
304 |
{{ num/2 }}
305 |
{{ (5*5)/10 }}
306 |
{{ 'x'*3 }}
307 |
{{ (5+3)*(7-2) }}
308 |
309 |
310 |
311 |
14. Filtros de Template
312 |
{{ langs|length }}
313 |
{{ langs|lower }}
314 |
{{ langs|upper }}
315 |
316 |
317 |
318 |
15. Filtros de Template - join
319 |
{{ langs|join(', ') }}
320 |
321 |
322 |
323 |
16. Escaping
324 | {{ html }}
325 | {{ html|safe }}
326 | {{ malicioso }}
327 | ```
328 |
329 | Podemos observar que:
330 |
331 | 1. Imprimimos o valor da variável **num**
332 | 2. Percorremos e imprimimos os elementos da lista **lang**
333 | 3. Percorremos e imprimimos os elementos da lista **lang** juntamente com seu índice
334 | 4. Percorremos o dicionário **personagens** e imprimimos os pares chave:valor
335 | 5. Acessamos o primeiro elemento da lista **lang**
336 | 6. Acessamos o elemento de chave **Akira** do dicionário **personagens**
337 | 7. Acessamos o atributo **nome** do objeto **app**
338 | 8. Desempacotamos os valores da tupla **cores** e os acessamos individualmente através das variáveis **a**, **b** e **c**
339 | 9. Chamamos a função **cubo()** para calcular o cubo dos valores **3** e **2**
340 | 10. Invocamos o método **endereco** através do objeto **app**
341 | 11. Aqui utilizamos o condicional **if** que testa se a variável **online** é True, uma vez que ela True, o conteúdo 'Usuário se encontra online' é apresentado
342 | 12. Comparamos a variável **num** com diversos valores
343 | 13. Testamos diversas expressões matemáticas
344 | 14. Utilizamos os **Filtros de Template** para modificar a apresentação de nossos dados
345 | 15. Utilizamos o filtro **join()** para transformar nossa lista em uma String
346 | 16. Exibimos os valores das variáveis **html** e **malicioso**, perceba que o filtro **safe** nos permite indicar que é seguro renderizar os dados, caso ele não seja utilizado, tags html e scripts não serão renderizados em nossa página por motivos de segurança.
347 |
348 | Finalmente vamos executar nossa aplicação através do comando `python app.py` e vamos navegar até `http://127.0.0.1:5000/jinja`. Veja como são exibidos nossos dados e experimente da forma que melhor lhe agrade.
349 |
350 | ## Herança de Template
351 |
352 | A Herança de Template nos permite criar um arquivo que servirá como o **layout** principal de nossa aplicação, de forma que não seja necessário ficarmos repetindo código desnecessariamente. Por exemplo, em aplicações web é muito como a existência de uma barra de navegação que contém diversas rotas, para não precisarmos ficar repetindo blocos html podemos usar o conceito de herança. Para entendermos melhor vamos então criar um arquivo `layout.html` em nosso diretório `templates` com o seguinte conteúdo
353 |
354 | ```html
355 |
356 |
357 | Aplicação Flask
358 |
359 |
360 | Navegar para Jinja: Jinja
361 |
362 | {% block conteudo %}{% endblock %}
363 |
364 |
365 | ```
366 |
367 | Veja que utilizamos o comando **block** que define um bloco com o nome **conteudo** arbitrariamente escolhido por nós, este bloco será capaz de receber conteúdo de outros templates. Agora vamos editar nosso arquivo `index.html` para que ele possa herdar as propriedades de nosso `layout.html`
368 |
369 | ```html
370 | {% extends 'layout.html' %}
371 |
372 | {% block conteudo %}
Olá {{ nome }}
{% endblock %}
373 | ```
374 |
375 | Através do comando **extends** indicamos que este arquivo irá herdar as propriedades de nosso `layout.html` e dentro do bloco **conteudo** nós definimos o HTML a ser exibido. Lembrando que é necessário fecharmos o bloco com **endblock**. Agora podemos executar nossa aplicação com o comando `python app.py` para vermos o nosso **layout** em ação.
376 |
377 | Caso queira experimentar o código em tempo real: https://repl.it/@theakira/Flask-Templates
378 |
379 | ## Sumarizando
380 |
381 | Neste capítulo:
382 |
383 | - Estudamos fundamentos sobre Template Engines
384 | - Trabalhamos com Jinja2 em Flask
385 | - Passamos variáveis para nosso Template
386 | - Apresentamos dados em nosso Template
387 | - Criamos um Layout principal para nossa simples Aplicação
388 |
389 | Agora que já temos o domínio dos Templates vamos estudar os Arquivos Static que são um aspecto importante em uma aplicação.
--------------------------------------------------------------------------------
/capitulos/Formulários Web.md:
--------------------------------------------------------------------------------
1 | # Formulários Web
2 |
3 | ## Introdução
4 |
5 | **[Formulários HTML](https://en.wikipedia.org/wiki/Form_(HTML))** são um dos principais pontos de **interação** entre um usuário e um site ou aplicativo. Eles permitem que os usuários enviem dados para nossa aplicação. Na maioria das vezes, os dados são enviados para o servidor Web, mas a página Web também pode interceptá-los para usá-los por conta própria.
6 |
7 | Um formulário HTML é feito de um ou mais *widgets*. Esses *widgets* podem ser campos de texto (linha única ou multilinha), caixas de seleção, botões, caixas de checagem ou botões de opção. Na maioria das vezes, esses widgets são combinados com um rótulo que descreve sua finalidade - os rótulos implementados adequadamente podem instruir claramente os usuários cegos e com visão sobre o que inserir em uma entrada de formulário.
8 |
9 | Veja todos os tipos possíveis de input: **[html_form_input_types](https://www.w3schools.com/html/html_form_input_types.asp)**
10 |
11 | A principal diferença entre um formulário HTML e um documento HTML regular é que, na maioria das vezes, os dados coletados pelo formulário são enviados para um servidor Web. Nesse caso, você precisa configurar um servidor da Web para receber e processar os dados, felizmente Flask já traz todos esses elementos para nós.
12 |
13 | Exemplo de um simples Formulário Web
14 |
15 | 
16 |
17 | Este formulário nos reflete o seguinte código HTML:
18 |
19 | ```html
20 |
31 | ```
32 |
33 | Perceba que definimos uma tag `
74 | {% endblock %}
75 | ```
76 |
77 | Executamos nossa aplicação com o comando `python app.py` e navegamos até `http://127.0.0.1:5000/formulario` para ver que tudo está funcionando corretamente, exceto que, podemos perceber que nosso formulário ainda não apresenta nenhuma funcionalidade, caso venhamos a tentar enviar dados através dele, nos será apresentado um erro **Method Not Allowed**, isto ocorre pois nossa aplicação não está apta a receber requisições **[POST](https://en.wikipedia.org/wiki/POST_(HTTP))**. Para resolvermos esse problema vamos alterar nossa função **formulario()** de forma que ela possa capturar esses dados. Para isso vamos editar nosso arquivo `app.py`
78 |
79 | Primeiramente vamos começar alterando os **imports** de nossa aplicação
80 |
81 | ```python
82 | from flask import Flask, render_template, request, redirect
83 | ```
84 |
85 | - A função **request()** nos possibilitará trabalharmos com os dados enviados através do formulário
86 | - A função **redirect()** nos possibilitará redirecionarmos o usuário para uma URL após a submissão do formulário
87 |
88 | Agora vamos alterar a função **formulario()** de forma que possamos receber os dados do usuário
89 |
90 | ```python
91 | @app.route('/formulario', methods=['GET','POST'])
92 | def formulario():
93 | if request.method == 'POST':
94 | req = request.form
95 |
96 | nome = req['nome']
97 | email = req.get('email')
98 | senha = request.form['senha']
99 |
100 | print(nome,email,senha)
101 |
102 | return redirect(request.url)
103 |
104 | return render_template('formulario.html')
105 | ```
106 |
107 | - Adicionamos um **if** que será avaliado como **True** caso seja uma requisição POST
108 | - Uma vez que a requisição é POST, vamos guardar na variável **req** uma estrutura de dados chamada de que é muito similar a um dicionário Python, através dessa estrutura vamos poder acessar cada campo de nosso formulário.
109 | - Através de **req['nome']** acessamos o campo nome e guardamos na variável **nome**
110 | - Através de **req.get('email')** acessamos o campo email e guardamos na variável **email**
111 | - Através de **request.form['senha']** acessamos o campo senha e guardamos na variável **senha**
112 | - Utilizamos a função **print()** para imprirmirmos os dados enviados pelo usuário (**Importante**: Os dados serão impressos em nosso terminal, onde nosso servidor está executando)
113 | - Por fim retornamos o usuário para a mesma URL através da função **redirect()** passando como parâmetro **request.url** que representa a própria URL da página.
114 | - Se eventualmente não estivermos lidando com uma requisição POST, significa que o usuário fez uma requisição GET, em outras palavras, ele deseja ver o conteúdo da página, nesse caso renderizamos o template através da função **render_template()**
115 |
116 | Momento de testarmos nossa aplicação, vamos executá-la através do comando `python app.py` e vamos visitar a URL: `http://127.0.0.1:5000/formulario`. Se fizermos uma submissão do formulário poderemos observar que eles serão impressos em nosso terminal, muito legal, não? Agora já conseguimos capturar os dados enviados através de formulários por usuários!
117 |
118 | Vamos agora aperfeiçoar nosso conhecimento e trabalhar com a biblioteca **[Flask-WTF](https://flask-wtf.readthedocs.io/en/stable/)**, esta que nos possibilitará o gerenciamento de nossos formulários de forma muito mais simples e amigável e também irá colaborar com a segurança de nossa aplicação.
119 |
120 | ## Formulários com Flask-WTF
121 |
122 | Para podermos utilizar a Biblioteca **Flask-WTF** em nossa aplicação é necessário instalarmos ela, recomendamos você fazer a instalação dela com o seu Ambiente Virtual ativado, sendo assim, execute este comando
123 |
124 | ```
125 | pip install flask-wtf
126 | ```
127 |
128 | Uma vez instalada com sucesso, agora podemos utilizar ela em nosso projeto, vamos então fazer algumas alterações em nosso arquivo `app.py`, porém, antes disso é importante dizermos que embora a estrutura de nossa aplicação pareça um pouco desorganizada, é porque ainda não entramos no aspecto de como estruturar nossa aplicação, por enquanto estamos focando apenas nos fundamentos, que são essenciais para executarmos diversos trabalhos, sendo assim vamos continuar trabalhando no arquivo `app.py`
129 |
130 | Primeiramente vamos alterar nossos **imports** para que possamos trabalhar com novas funcionalidades
131 |
132 | ```python
133 | from flask import Flask, render_template, request, redirect, session, url_for
134 | from flask_wtf import FlaskForm
135 | from wtforms import StringField, BooleanField, SelectField, TextField, TextAreaField, SubmitField
136 | from wtforms.validators import DataRequired
137 | ```
138 |
139 | - Na primeira linha importamos **session** que nos permitará trabalhar com variáveis de sessão temporárias e a função **[url_for()](https://flask.palletsprojects.com/en/1.0.x/api/#flask.url_for)** que possibilita construirmos uma URL para uma função específica de nossa aplicação
140 | - Na segunda linha importamos **FlaskForm** de **wtforms** que será essencial para construirmos nosso Formulário
141 | - Na terceira linha importamos todos os campos que vamos utilizar em nosso formulário
142 | - Por fim, na quarta linha importamos o validador **DataRequired** que irá indicar que determinado campo deve ser obrigatoriamente preenchido
143 |
144 | Agora que todos os nossos **imports** estão organizados corretamente é o momento de definirmos o nosso formulário que será representado através de uma Classe Python que chamaremos de **LivroForm**, novamente vamos editar o arquivo `app.py`
145 |
146 | ```python
147 | app.config['SECRET_KEY'] = 'chavesecreta'
148 |
149 | class LivroForm(FlaskForm):
150 | titulo = StringField('Titulo do Livro', validators=[DataRequired()])
151 | autor = StringField('Nome do Autor', validators=[DataRequired()])
152 | vivo = BooleanField('Autor está Vivo?')
153 | genero = SelectField('Gênero:',choices=[('aventura','Aventura'),('cientifico','Cientifico'),('romance','Romance')])
154 | resumo = TextAreaField()
155 | submit = SubmitField('Enviar')
156 | ```
157 |
158 | 1. Iniciamos atribuindo o valor 'chavesecreta' à nossa variável de configuração **SECRET_KEY**, ela irá nos auxiliar na proteção contra ataques **[Cross-Site Request Forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery)**, importante lembrarmos que não é uma boa prática o que estamos fazendo, o ideal é guardarmos esses dados em uma **[Environment Variable](https://en.wikipedia.org/wiki/Environment_variable)**, vamos deixar dessa forma para simplicidade, uma vez que nosso objetivo é entender os fundamentos.
159 | 2. Através da palavra-chave **class** definimos nosso formulário com o nome de **LivroForm** que recebe como parâmetro **FlaskForm**, este que importamos mais cedo
160 | 3. Definimos a variável **titulo** que será um Campo de String com a label 'Titulo do Livro', veja que utilizamos o validador **DataRequired()** para indicar que o campo deve ser obrigatoriamente preenchido
161 | 4. Definimos a variável **autor** que será um Campo de String com a label 'Nome do Autor', veja que utilizamos o validador **DataRequired()** para indicar que o campo deve ser obrigatoriamente preenchido
162 | 5. Definimos a variável **vivo** que será um Campo Boolean com a label 'Autor está Vivo?'
163 | 6. Definimos a variável **genero** que será um Campo de Seleção contendo as opções 'Aventura', 'Cientifico' e 'Romance'
164 | 7. Definimos a variável **resumo** que será um Campo de Área de Texto
165 | 8. Por fim definimos a variável **submit** que representará nosso botão de submissão contendo a label 'Enviar'
166 |
167 | Agora que temos nosso formulário definido, vamos definir **duas novas rotas** para experimentarmos. Novamente editamos nosso arquivo `app.py`:
168 |
169 | ```python
170 | @app.route('/wtf', methods=['GET','POST'])
171 | def wtf():
172 | form = LivroForm()
173 | if form.validate_on_submit():
174 | session['titulo'] = form.titulo.data
175 | session['autor'] = form.autor.data
176 | session['vivo'] = form.vivo.data
177 | session['genero'] = form.genero.data
178 | session['resumo'] = form.resumo.data
179 |
180 | return redirect(url_for('obrigado'))
181 |
182 | return render_template('wtf.html', form=form)
183 |
184 | @app.route('/obrigado')
185 | def obrigado():
186 | return render_template('obrigado.html')
187 | ```
188 |
189 | - A primeira rota **wtf()** vai processar o formulário (caso seja uma requisição POST) ou vai renderizar o formulário (caso seja uma requisição GET)
190 | - A segunda rota **obrigado()** vai ser ativada através da função **redirect()**, nela iremos exibir os dados submetidos pelo usuário através das variáveis de sessão
191 | - A variável **form** irá guardar o objeto que representa nosso Formulário, nesse caso **LivroForm**
192 | - Perceba que as variáveis serão atribuídas somente se o formulário for válido, por isso utilizamos o método **validate_on_submit()**
193 | - Caso o formulário não seja válido ou a requisição seja **GET** vamos apenas renderizar o template com o formulário
194 |
195 | Agora precisamos criar nossos templates para que possamos exibir o formulário e os dados submetidos, para isso vamos criar os arquivos `wtf.html` e `obrigado.html` na pasta `templates`
196 |
197 | `wtf.html`
198 |
199 | ```html
200 |
Cadastro de Livros Flask-WTF
201 |
202 |
218 | ```
219 |
220 | - Este template espera um objeto **form** que foi instanciado de **LivroForm()** como já vimos anteriormente
221 | - O elemento HTML `