1.安裝
1.1 建立虛擬環境
mkdir myproject
cd myproject
python3 -m venv venv
1.2 進入虛擬環境
. venv/bin/activate
1.3 安裝 flask
pip install Flask
2.上手
2.1 最小 Demo
將下列程式碼儲存為 hello.py
:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
執行上述程式碼:
export FLASK_APP=hello
flask run
這樣訪問:http://127.0.0.1:5000 會看到 Hello, World!
2.2 基本知識
這裡有 flask 的基本知識(非常重要的基礎,大家可以自己看:連結
-
HTML Escaping (利用 Jinja,參考:連結
-
Routing (下面幾個例子)
@app.route('/') def index(): return 'Index Page' @app.route('/hello') def hello(): return 'Hello, World' @app.route('/user/<username>') def show_user_profile(username): # show the user profile for that user return f'User {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 f'Post {post_id}' @app.route('/path/<path:subpath>') def show_subpath(subpath): # show the subpath after /path/ return f'Subpath {escape(subpath)}'
-
HTTP Methods
@app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': else:
-
Static Files (
url_for('static', filename='style.css')
) -
Rendering Templates (這個參考之前的 Jinja)
-
File Uploads、Cookies、Redirects and Errors、About Responses、APIs with JSON、Sessions、Message Flashing、Logging 這些等我們實際用到時再過來看
3.解構官網指導 Demo
第 1 節教大家如何利用 python 虛擬環境,快速構建 flask 環境;第 2 節帶著大家簡單熟悉了 flask 的程式設計規則(或風格)。
大家在著手本節時,務必將第 2 節中的基礎的程式碼跟著官網敲一下!因為,這一節我們不是由簡到難一步步搭建 flask 伺服器,而是直接拿搭建好的反過來分析。
3.1 克隆與程式碼架構分析
$ git clone https://github.com/pallets/flask
$ cd flask
$ cd examples/tutorial
程式碼目錄結構如下:
➜ tutorial git:(main) tree -L 4
.
├── flaskr
│ ├── __init__.py
│ ├── db.py
│ ├── schema.sql
│ ├── auth.py
│ ├── blog.py
│ │
│ ├── templates
│ │ ├── base.html
│ │ ├── auth
│ │ │ ├── login.html
│ │ │ └── register.html
│ │ └── blog
│ │ ├── create.html
│ │ ├── index.html
│ │ └── update.html
│ │
│ └── static
│ └── style.css
│
├── MANIFEST.in
└── setup.py
3.2 入口檔案 init.py
def create_app(test_config=None):
"""Create and configure an instance of the Flask application."""
# 1-建立一個 Flask 例項
# 並設定一些 APP 需要用到的引數
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
# a default secret that should be overridden by instance config
SECRET_KEY="dev",
# store the database in the instance folder
DATABASE=os.path.join(app.instance_path, "flaskr.sqlite"),
)
# 2-測試用的
if test_config is None:
# load the instance config, if it exists, when not testing
app.config.from_pyfile("config.py", silent=True)
else:
# load the test config if passed in
app.config.update(test_config)
# 3-建立一個資料夾,用來存 DB 執行時的產生的檔案
# ensure the instance folder exists
try:
os.makedirs(app.instance_path)
except OSError:
pass
@app.route("/hello")
def hello():
return "Hello, World!"
# register the database commands
# 3.3 資料庫設定(為 flask 新增一個 init_db 命令,這樣直接敲 flask init_db 就能生成表)
from flaskr import db
db.init_app(app)
# apply the blueprints to the app
# #### 3.4 藍圖和檢視(基於藍圖來管理組織檢視,檢視註冊到藍圖,藍圖註冊到應用)
from flaskr import auth, blog
app.register_blueprint(auth.bp)
app.register_blueprint(blog.bp)
# make url_for('index') == url_for('blog.index')
# in another app, you might define a separate main index here with
# app.route, while giving the blog blueprint a url_prefix, but for
# the tutorial the blog will be the main index
app.add_url_rule("/", endpoint="index")
return app
3.3 資料庫設定
該專案採用了 SQLite 作為資料庫(Python 內建了,免去安裝和配置工作)。
- SQL 檔案 schema.sql
SQLite 的資料儲存在表格中,在向表格增刪改查資料前,需要先建表。該專案中的 schema.sql 編寫了建表的 SQL 語句。分別建立了一個 user 表和 post 表。
DROP TABLE IF EXISTS user;
DROP TABLE IF EXISTS post;
CREATE TABLE user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL
);
CREATE TABLE post (
id INTEGER PRIMARY KEY AUTOINCREMENT,
author_id INTEGER NOT NULL,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
title TEXT NOT NULL,
body TEXT NOT NULL,
FOREIGN KEY (author_id) REFERENCES user (id)
);
2)與資料庫建立連線與斷開
def get_db():
"""Connect to the application's configured database. The connection
is unique for each request and will be reused if this is called
again.
"""
if "db" not in g:
g.db = sqlite3.connect(
current_app.config["DATABASE"], detect_types=sqlite3.PARSE_DECLTYPES
)
g.db.row_factory = sqlite3.Row
return g.db
def close_db(e=None):
"""If this request connected to the database, close the
connection.
"""
db = g.pop("db", None)
if db is not None:
db.close()
g 是一個特殊結構,對於每次請求,會產生一個。
3)資料庫初始化(生成表)
第 1 節的 schema.sql 用於建表,那麼如何執行其中的建表命令呢? db.py 中的 init_db 就是幹這個事情的。
def init_db():
"""Clear existing data and create new tables."""
db = get_db() # 獲取資料庫(如果沒有則建立)
# 讀取 schema.sql 中的 SQL 命令,並用 db.executescript 執行 SQL 命令
with current_app.open_resource("schema.sql") as f:
db.executescript(f.read().decode("utf8"))
4)將 init_db 註冊為 flask 命令
由於資料庫初始化並不需要每次啟動資料庫時執行(不屬於執行時需要執行的函式),我們需要將註冊成 flask 一個指令,只要在命令列中敲 flask init-db
就能夠執行 init_db
,其實現方法如下:
@click.command("init-db")
@with_appcontext
def init_db_command():
"""Clear existing data and create new tables."""
init_db()
click.echo("Initialized the database.")
def init_app(app):
"""Register database functions with the Flask app. This is called by
the application factory.
"""
app.teardown_appcontext(close_db) # 在返回響應後進行清理時呼叫該函式
app.cli.add_command(init_db_command) # 新增一個可以用flask命令呼叫的新命令
這樣,執行完之後,flask.sqlite 檔案將會出現在 instance 資料夾。
3.4 藍圖和檢視
藍圖是一種組織一組相關檢視和其他程式碼的方法。它們不是直接嚮應用程式註冊檢視和其他程式碼,而是向藍圖註冊。然後,當藍圖在factory函式中可用時,它將在應用程式中註冊。
該專案中有兩個藍圖:auth 和 blog
bp = Blueprint("auth", __name__, url_prefix="/auth") # in auth.py
bp = Blueprint("blog", __name__) # in blog.py
引數分別是:藍圖的名字,import_name(一般為 __name__),url 字首
1)auth 檢視
這裡主要有三個路由:
@bp.route("/register", methods=("GET", "POST"))
def register():
...
@bp.route("/login", methods=("GET", "POST"))
def login():
...
@bp.route("/logout")
def logout():
2)blog 檢視
這裡主要有四個路由:
@bp.route("/")
def index():
...
@bp.route("/create", methods=("GET", "POST"))
@login_required
def create():
...
@bp.route("/<int:id>/update", methods=("GET", "POST"))
@login_required
def update(id):
...
@bp.route("/<int:id>/delete", methods=("POST",))
@login_required
def delete(id):
...
3)註冊檢視中各個功能實現介紹
-
註冊
註冊邏輯為:首先從 POST 中獲取 username 和 password,然後呼叫資料庫插入操作:
username = request.form["username"]
password = request.form["password"]
db.execute("INSERT INTO user (username, password) VALUES (?, ?)", (username, generate_password_hash(password)),)
-
登入
登入邏輯為:首先從 POST 中獲取 username 和 password,然後呼叫資料庫查詢操作,獲取該使用者的密碼,然後進行密碼匹配:
user = db.execute("SELECT * FROM user WHERE username = ?",username,)).fetchone()
check_password_hash(user["password"], password)
密碼匹配後,需要建立 session:
if error is None: # store the user id in a new session and return to the index session.clear() session["user_id"] = user["id"] return redirect(url_for("index"))
-
登出
登出需要清空 session:
session.clear()
-
Session
Session 邏輯如下:註冊一個方法,讓其在任何 URL 請求之前執行,在其中做 Session 管理:
@bp.before_app_request def load_logged_in_user(): user_id = session.get('user_id') if user_id is None: g.user = None else: g.user = get_db().execute( 'SELECT * FROM user WHERE id = ?', (user_id,) ).fetchone()
-
其他 View 使用認證
其他 View 也想使用認證該如何做?在 auth.py 中實現 login_required 函式,判斷 user 是否為空,如果為空,則跳轉到登入頁面:
def login_required(view): @functools.wraps(view) def wrapped_view(**kwargs): if g.user is None: return redirect(url_for('auth.login')) return view(**kwargs) return wrapped_view
4)部落格檢視中各個功能實現介紹
-
展示所有部落格
邏輯如下:執行資料庫查詢操作,獲取所有部落格,然後載入:@bp.route("/") def index(): """Show all the posts, most recent first.""" db = get_db() posts = db.execute( "SELECT p.id, title, body, created, author_id, username" " FROM post p JOIN user u ON p.author_id = u.id" " ORDER BY created DESC" ).fetchall() return render_template("blog/index.html", posts=posts)
-
建立部落格
邏輯如下:函式前加上@login_required
字首,這樣就能自動判斷是否已經登入,否則跳到登入頁面;建立部落格就是獲取標題和內容,然後呼叫插入命令,進行插入:@bp.route("/create", methods=("GET", "POST")) @login_required def create(): """Create a new post for the current user.""" if request.method == "POST": title = request.form["title"] body = request.form["body"] error = None if not title: error = "Title is required." if error is not None: flash(error) else: db = get_db() db.execute( "INSERT INTO post (title, body, author_id) VALUES (?, ?, ?)", (title, body, g.user["id"]), ) db.commit() return redirect(url_for("blog.index")) return render_template("blog/create.html")
-
更新和刪除部落格
更新和刪除部落格,需要傳入一個 id,然後有一個內部函式用於判斷該 id 是否存在:def get_post(id, check_author=True): """Get a post and its author by id. Checks that the id exists and optionally that the current user is the author. :param id: id of post to get :param check_author: require the current user to be the author :return: the post with author information :raise 404: if a post with the given id doesn't exist :raise 403: if the current user isn't the author """ post = ( get_db() .execute( "SELECT p.id, title, body, created, author_id, username" " FROM post p JOIN user u ON p.author_id = u.id" " WHERE p.id = ?", (id,), ) .fetchone() ) if post is None: abort(404, f"Post id {id} doesn't exist.") if check_author and post["author_id"] != g.user["id"]: abort(403) return post
因此,更新的邏輯如下:
@bp.route("/<int:id>/update", methods=("GET", "POST")) @login_required def update(id): """Update a post if the current user is the author.""" post = get_post(id) if request.method == "POST": title = request.form["title"] body = request.form["body"] error = None if not title: error = "Title is required." if error is not None: flash(error) else: db = get_db() db.execute( "UPDATE post SET title = ?, body = ? WHERE id = ?", (title, body, id) ) db.commit() return redirect(url_for("blog.index")) return render_template("blog/update.html", post=post)
刪除的邏輯如下:
@bp.route("/<int:id>/delete", methods=("POST",)) @login_required def delete(id): """Delete a post. Ensures that the post exists and that the logged in user is the author of the post. """ get_post(id) db = get_db() db.execute("DELETE FROM post WHERE id = ?", (id,)) db.commit() return redirect(url_for("blog.index"))
4.其他
其他還有一些,是大家玩熟了之後才需要看的:
5.跑起 DEMO
最後,我們跑起 Demo 看看效果:
1)在 tutorial 目錄下,建立虛擬環境,並安裝 Flask:
python3 -m venv venv
. venv/bin/activate
pip install Flask
2)以開發者方式執行:
export FLASK_APP=flaskr
export FLASK_ENV=development
flask init-db
flask run
效果如下:
參考連結
[1]. 本文原始碼
[2]. Flask 文件主頁
[3]. tutorial 主頁
[4]. Jinja 模板入門
[5]. python django web 開發
[6]. 真正搞明白Python中Django和Flask框架的區別
: 這篇是在大家熟悉基本的 flaskr 之後,進行稍微大一點的專案開發的指導,裡面涉及到資料庫、藍圖等...
如果覺得不錯,幫忙點個支援哈~