---
title: Flask 教學 # 簡報的名稱
tags: Web, Python, Flask # 簡報的標籤
slideOptions: # 簡報相關的設定
theme: solarized # 顏色主題
transition: 'fade' # 換頁動畫
---
# Flask 教學
----
Flask 是個輕量級的程式語言,其簡單的程式碼就可以架構出一個簡易的http服務應用。
----
## 簡易用 Flask 架設的網站
```python=
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == '__main__':
app.debug = True
app.run()
```
----
### 說明各部份參數
---
# 路由 & 參數
一個網站裏面,會有各式各樣的路徑,而這些路徑,在網站裏面稱作路由,會決定哪個網址由哪段程式碼處理
----
```python=
@app.route('/')
def index():
return 'Index Page'
@app.route('/hello')
def hello():
return 'Hello, World'
```
----
除了這些靜態路由,當然還有所謂的動態路由
```python=
@app.route('/user/<username>')
def show_user_profile(username):
# show the user profile for that user
return 'User {}'.format(escape(username))
@app.route('/post/<int:post_id>')
def show_post(post_id):
# show the post with the given id, the id is an integer
return 'Post {}'.format(post_id)
```
----
目前支援以下的參數
| type | 說明 |
| ------:|:---------------------------------------:|
| string | (default) 除了 `/` 的任意字元組成的字串 |
| int | 正整數 |
| float | 正浮點數 |
| path | string + `/` |
| uuid | UUID 字串 |
---
# Templates
Flask 預設使用 [JinJa2](https://jinja.palletsprojects.com/en/2.11.x/templates/) 模板排版引擎
----
```python=
from flask import render_template
@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
return render_template('hello.html', name=name)
```
----
模板要放在 templates 的資料夾內
```
/application.py
/templates
/hello.html
```
----
以下是一個簡易模板的內容
```htmlmixed=
<!doctype html>
<title>Hello from Flask</title>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello, World!</h1>
{% endif %}
```
----
使用 Flask + Jinja2時,在模板內你可以存取 `request`, `session` and `g objects`
----
Flask 允許通過 function 去獲得網址,這點可以讓網址產生是動態的,在網頁裡可以顯示出該段的網址
---
# Method
## Get/Post/Put/Patch/Delete
----
一般網頁只能使用 Get/Post
Put/Patch/Delete 需要使用 JavaScript 來輔助
----
```python=
@app.route('/method', methods=['POST', 'GET'])
def method():
method = request.method
data = request.form
return render_template('method.html',
method=method, data=data)
```
----
method.html
```htmlmixed=
<!doctype html>
<html>
<head> <title>Hello from Flask</title> </head>
<body> <h1>Hello {{ method }}!</h1>
<form method="POST">
<label for="d1">d1:</label><input name="d1" id="d1">
<button type="submit">POST Submit</button>
</form>
<ol>
{% for k, v in data.items() %}
<li>{{ k }}: {{ v }}</li>
{% endfor %}
</ol>
</body> </html>
```
----
### 題外話 Restful API
現在有很多單頁式網站,全部的資料都由 AJAX 去做更新,沒有跳頁轉換的白頁問題,而 AJAX 目前流行的有 Restful API、Graph API 等。
----
何謂 Restful API 呢?就是全部的網址都是名詞,而要怎麼分辨目前的增刪修的行為呢?,這時候之前講到的 GET/POST/PATCH/PUT/DELETE 就是重點了
----
而且這種方式的回應都大多只需要回應
200 OK,
201 CREATE,
204 NO CONTENT,
404 NOT FOUND,
而不用煩惱轉址問題(前端處理)
---
以上為最基本的 Flask 說明,我們補個資料庫連線後,就開始做一個小網站吧~
就來個最簡單的縮短網址服務吧
----
首先是資料庫連線
用到了套件 flask-SQLAlchemy pymysql
```bash=
pip install flask-sqlalchemy pymysql
```
----
想一想,縮網址服務需要哪些資料庫欄位
1. 目標網址 (type: Text)
2. 點擊次數 (type: Integer)
3. 建立者 (type: String/User)
----
```python=
from flask import Flask, request, render_template
from flask import redirect, url_for
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = \
'sqlite:///reurl.db'
db = SQLAlchemy(app)
class Url(db.Model):
id = db.Column(db.Integer, primary_key=True)
url = db.Column(db.Text, nullable=False)
clicks = db.Column(db.Integer, default=0)
```
----
我們在建立一個檔案 create_table.py,並執行他
資料表就建起來了
```python=
from main import db
db.drop_all()
db.create_all()
```
----
再來呢,建一個路由來建立短網址吧
```python=
@app.route('/url', methods=['GET', 'POST'])
def reUrl():
if request.method == 'POST':
url = request.form.get('url')
error_message = None
short_url = None
if url:
u = Url(url=url)
db.session.add(u)
db.session.commit()
db.session.refresh(u) # 要 refresh 才能拿到 id 喔
short_url = url_for('sUrl',
id=u.id, _external=True)
```
----
```python=15
else:
error_message = 'Error: 網址為空'
return render_template('create_url.html',
msg=error_message, short_url=short_url)
return render_template('create_url.html')
```
----
create_url.html
```htmlembedded=
{% if short_url %}
<span>短網址已建立:
<a href='{{ short_url }}'>{{ short_url }}</a>
</span>
{% endif %}
{% if msg %}
<span>MSG: {{ msg }}</span>
{% endif %}
<form method="POST">
<label for="url">URL:</label>
<input name="url" id="url">
<button type="submit">Submit</button>
</form>
```
----
建立完了,總要幫我們轉址,並增加點擊次數
```python=
@app.route('/u/<int:id>', methods=['GET'])
def sUrl(id):
url = Url.query.get(id)
url.clicks += 1
db.session.commit()
return redirect(url.url)
```
----
來試看看吧
http://127.0.0.1:5000/url
----
建一個頁面列出現在的縮網址吧
```python=
@app.route('/urls', methods=['GET'])
def getUrlList():
urls = Url.query.all()
return render_template('list_url.html', urls=urls)
```
----
list_url.html
```htmlmixed=
<table>
<thead>
<tr>
<th>id</th>
<th>url</th>
<th>origin url</th>
<th>clicks</th>
</tr>
</thead>
```
----
```htmlmixed=10
{% for url in urls %}
<tr>
<td>{{ url.id }}</td>
<td>
<a href="{{ url_for('sUrl', id=url.id) }}">
{{ url_for('sUrl', id=url.id, _external=True) }}
</a>
</td>
<td>{{ url.url }}</td>
<td>{{ url.clicks }}</td>
</tr>
{% endfor %}
</table>
```
----
http://127.0.0.1:5000/urls

---
# Session
----
我們在前面再加入一個 User 的類別吧
```python=
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), nullable=False)
username = db.Column(db.String(80), nullable=False)
password = db.Column(db.String(80), nullable=False)
def __repr__(self):
return '<User {}>'.format(self.username)
```
----
並修改 Url 類別,加入以下欄位
```python=
creater_id = db.Column(db.Integer,
db.ForeignKey('user.id'),
nullable=True)
creater = db.relationship('User',
backref=db.backref('urls', lazy=True))
```
----
再來,安全性從小做起,密碼要單向 hash,所以我們裝 `flask-bcrypt`
```bash=
pip install flask-bcrypt
```
----
並在 `main.py` 最前方內加入
```python=
from flask import session
from flask_bcrypt import Bcrypt
app = Flask(__name__)
app.secret_key = 'eU\x88\x0f=\xa7\xf8\x8aqF\xfb\xb5E-\\\xf2'
bcrypt = Bcrypt(app)
```
----
並加入註冊頁面
```python=
@app.route('/register', methods=['GET', 'POST'])
def register():
msg = ''
if request.method == 'POST':
f_username = request.form.get('username')
f_password = request.form.get('password')
f_name = request.form.get('name')
if f_username and f_password and f_name:
b_password = \
bcrypt.generate_password_hash(f_password)\
.decode('utf-8')
```
----
```python=14
user = User(username=f_username,
password=b_password,
name=f_name)
db.session.add(user)
db.session.commit()
msg = 'Success: 帳號建立成功'
else:
msg = 'Error: 表單不能為空'
return render_template('register.html', msg=msg)
```
----
register.html
```htmlmixed=
{% if msg %}
<span>MSG: {{ msg }}</span>
{% endif %}
<form method="post">
<p><label>Name:</label>
<input type="text" name="name"></p>
<p><label>Userame:</label>
<input type="text" name="username"></p>
<p><label>Password:</label>
<input type="password" name="password"></p>
<p><input type="submit" value="Register"></p>
</form>
```
----
也要登入了
```python=
@app.route('/login', methods=['GET', 'POST'])
def login():
error_message = ''
if request.method == 'POST':
f_username = request.form.get('username')
f_password = request.form.get('password')
user = User.query.filter_by(username=f_username)\
.first()
if bcrypt.\
check_password_hash(user.password, f_password):
session['username'] = request.form['username']
return redirect(url_for('index'))
error_message = 'Error: 密碼錯誤'
return render_template('login.html', msg=error_message)
```
----
login.html
```htmlmixed=
{% if msg %}
<span>MSG: {{ msg }}</span>
{% endif %}
<form method="post">
<p><label>Userame:</label>
<input type="text" name="username"></p>
<p><label>Password:</label>
<input type="password" name="password"></p>
<p><input type="submit" value="Login"></p>
</form>
```
----
登出
```python=
@app.route('/logout')
def logout():
session.pop('username', None)
return redirect(url_for('index'))
```
----
修改一下首頁
```htmlmixed=
<h1>Hello Flask</h1>
<p>
{% if session.get('username') %}
<h2>Hello User: {{ session['username'] }}</h2>
<span><a href="{{ url_for('logout') }}">logout?</a></span>
{% else %}
<span><a href="{{ url_for('login') }}">Login</a></span>
<span><a href="{{ url_for('register') }}">Register</a></span>
{% endif %}
</p>
<ul>
<li><a href="{{ url_for('reUrl') }}">縮網址</a></li>
<li><a href="{{ url_for('getUrlList') }}">縮列表</a></li>
</ul>
```
----
再修改一下建立短網址的程式,把建立的人寫入進去
```python=8
user = None
if session.get('username'):
user = User\
.query.filter_by(
username=session.get('username')
) \
.first()
if url:
u = Url(url=url, creater=user)
```
----
接下來我們在短網址內加入
```htmlmixed=
<td>{{ url.creater.username }}</td>
```
---
# Next?
----
美化你的專案,學會 HTML5 + CSS3
----
佈署你的專案,可以使用 heroku
https://devcenter.heroku.com/articles/getting-started-with-python
---
# 結論
要學會一個東西,你要先有目標...
然後瘋狂踩雷
----
## 沒有動力怎麼辦?
VPS 先刷下去,為了不浪費錢
你就會很努力的天天做你的專案