[python][flask] Flask 入門(以一個博客後臺為例)

来源:https://www.cnblogs.com/zjutlitao/archive/2022/05/03/16218348.html
-Advertisement-
Play Games

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 將下列代碼 ...



目錄


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 內置了,免去安裝和配置工作)。

  1. 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].官方 Demo Github 倉庫

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 之後,進行稍微大一點的項目開發的指導,裡面涉及到資料庫、藍圖等...

如果覺得不錯,幫忙點個支持哈~


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 多線程筆記(一) 1. sleep()方法和yield()方法 共同點:讓當前線程釋放cpu資源,讓其他線程來運行 不同點:調用sleep()方法後,線程進入到TIMED_WAITING狀態,等待超時後進入RUNNABLE狀態,開始搶占CPU資源。調用yield()方法後,線程進入RUNNABLE狀 ...
  • pandas讀取Excel、csv文件中的數據時,得到的大多是表格型的二維數據,在pandas中對應的即為DataFrame數據結構。在處理這類數據時,往往要根據據需求先獲取數據中的子集,如某些列、某些行、行列交叉的部分等。可以說子集選取是一個非常基礎、頻繁使用的操作,而DataFrame的子集選取 ...
  • 8. 文件讀寫操作 1 #include<iostream> 2 #include<string> 3 #include<fstream> // 讀寫文件 頭文件 4 using namespace std; 5 6 // 文件操作 7 8 // 寫文件 9 void writefile() { 1 ...
  • 以下是我收集的一些問題,有的是網上摘錄的,有的是自己參加面試被問到的,有的是工作或學習時遇到的,等等。 為什麼要記錄這些呢? 一方面,我相信,這樣做對我自己的技術提升是有幫助的。在全文結構上我儘量**使問題連貫地形成知識體系**,而不是堆積的碎片,而且,每個問題我會儘量地給出答案。 另一方面,我希望... ...
  • VSCode開發環境配置 先到VSCode官網去下載適合自己系統的VSCode安裝軟體 VScode下載地址:https://code.visualstudio.com/Download ### 演示在WIndows下 安裝使用 (1)把vscode安裝軟體準備好 如果不清楚選64位還是32位可以在 ...
  • 文件操作(輸入輸出流) 文件操作的概述 程式運行時產生的數據都屬於零食數據,程式一旦運行結束,就會被釋放 通過文件可以將數據持久化 C++中對文件的操作包含頭文件(文件流) 文件類型分為兩種 文本文件:文件以文本的ASCII碼的形式存儲在電腦中 二進位文件:文件以文本的二進位形式存儲在電腦中,用 ...
  • string是C標準模板庫中專門用於字元串處理的數據結構類型。它並不是 C的基本數據類型,它是 C++標準模板庫中的一個“類”。若要使用 string 對象,則必須包含頭文件#include <string>。 初始化 常用的初始化有以下幾種,帶等號的是拷貝初始化, string str1("hel ...
  • 7. 多態 7.1 多態基本用法 1 #include<iostream> 2 using namespace std; 3 4 // 多態 5 6 // 動態多態滿足條件: 7 // 1.有繼承關係 8 // 2. 子類重寫父類的虛函數 9 // 10 // 動態多態使用 11 // 父類的指針或 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...