FLASK學習系列二-工廠函數和數據庫連接

flask的安裝請參見這個
項目的佈局
整個教程都是在flask_tutorial這個目錄下進行的。
依照系列一中的內容,先創建一個簡單的文件hello.py

from flask import Flask

app = Flask(__name__)


@app.route('/')
def hello():
    return 'Hello, World!'

但是隨着項目的增大,不可能將所有的代碼放在一個文件中。所以需要根據不同代碼文件的功能將其分爲不同的模塊,在這裏項目將會被分爲這樣的幾個模塊:

  • flaskr:包含應用代碼和文件
  • tests:測試模塊
  • venv:flask和其他依賴所安裝地方(虛擬環境)
  • 告訴python如何安裝你的項目的安裝指導
  • 版本控制工具
    項目的整體佈局:
    在這裏插入圖片描述

應用安裝

flask的應用實際上是一個flask類的實例,關於這個應用的任何東西,包括配置文件和url,都將會在這個類裏面註冊。

創建一個flask應用最爲直接的方法就是創建一個flask全局實例,但是隨着項目的增大這種方法會帶來很多的麻煩。
這裏我們並不會創建一個flask全局的實例,而是要在一個函數中創建。這個函數便是應用工廠函數,一切的配置,註冊,以及應用所需要的設置都會包含在這個文件中,最後這個應用將會被返回。

應用工廠函數
接下來我們將創建應用工廠函數,這個函數將有兩個功能,第一它會包含應用工廠;第二它會告訴pythonflaskr目錄應該被視爲一個包。

flaskr/init.py

import os

from flask import Flask


def create_app(test_config=None):
    # create and configure the app
    app = Flask(__name__, instance_relative_config=True)
    app.config.from_mapping(
        SECRET_KEY='dev',
        DATABASE=os.path.join(app.instance_path, 'flaskr.sqlite'),
    )

    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.from_mapping(test_config)

    # ensure the instance folder exists
    try:
        os.makedirs(app.instance_path)
    except OSError:
        pass

    # a simple page that says hello
    @app.route('/hello')
    def hello():
        return 'Hello, World!'

    return app
  1. app = Flask(name,instance_relative_config=True)這句創建了flask實例,name 是現在的python模塊的名字,應用需要知道它的安裝的路徑
    ;instance_relative_config=True 告訴應用是和instance文件夾有關的,實例文件夾是在flaskr外面,並且其保留着不能提交給版本控制的本地數據
  2. app.config.from_mapping() 設置了應用將會用到的配置文件。其中secret_key是用於保證數據安全的密鑰,但是這個密鑰在項目部署是將會被一個隨機值覆蓋;DATABASE是數據庫文件保存的地方。
  3. app.config.from_pyfile()將會覆蓋在config.py中得來的默認的配置
  4. os.makedirs()確保了app.instance_path存在,flask並不會自動創建實例文件夾,但是它是必要的,因爲項目將會在這裏創建數據庫文件。
  5. @app.route(),創建了一個簡單的路由,它建立了url和/hello之間的聯繫,並在最後返回一個響應。

運行應用

在終端中的flaskr當前路徑下運行以下代碼(window版,其他版本參見

set FLASK_APP=flaskr
set FLASK_ENV=development
flask run

輸出這個樣:

* Serving Flask app "flaskr"
* Environment: development
* Debug mode: on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 855-212-761

這時便可以訪問http://127.0.0.1:5000/hello來查看結果。

定義和訪問數據庫
本次的應用將會採用SQLite數據庫存儲用戶信息,python中在sqlite3中有內置的支持。
SQLite非常方便,原因在於其不需要設置一個一個隔離的數據庫服務器,但是如果在同一時間有併發的請求寫入數據庫的話,寫入放入速率就會降低。這一點會在大的項目中出現。

連接數據庫
數據庫的任何操作都是在連接好的基礎上實現的。在web應用中,數據庫連接多數發生請求到來時,在處理請求的某一個時間點連接會被創建,響應被髮送後連接會被關閉。

flaskr/db.py

import sqlite3

import click
from flask import current_app, g
from flask.cli import with_appcontext


def get_db():
    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):
    db = g.pop('db', None)

    if db is not None:
        db.close()

我們在系列一種稍微提到過g對象。g對象對於每一個請求來說都是獨一無二的,它的主要功能是存儲數據,這些數據可能會在請求的過程中會被用到。如果get_db在同一個請求中被第二次調用的話,連接會被重用而非重新建立一個連接。
上面代碼中:

  1. current_app是指向處理請求的flask應用的一個特殊的對象,因爲你使用的是一個工廠函數,所以在你調用這個函數之前,並不會有對象出現。current_app使用的原因就在於只有當應用已經創建的時候且其正在處理一個請求的時候,get_db纔會被調用。
  2. sqlite3.connect()建立了一個到文件的連接,這個文件被DataBase的配置鍵指向。這個文件在數據庫初始化之後纔會存在。
  3. sqlite3.Row 告訴連接返回的行起着字典的作用,這樣就可以通過名字來訪問列的數據。

創建表
SQLite中,數據是被存儲在表和列中的,這些在你存儲和檢索數據之前就需要被創建, Flaskr將會在user表中存儲user,在post表中存儲評論的信息。創建Sql文件的命令:

flaskr/schema.sql

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)
);

下面的函數將會返回sql命令給db.py

def init_db():
    db = get_db()

    with current_app.open_resource('schema.sql') as f:
        db.executescript(f.read().decode('utf8'))


@click.command('init-db')
@with_appcontext
def init_db_command():
    """Clear the existing data and create new tables."""
    init_db()
    click.echo('Initialized the database.')

上面代碼中:

  1. open_resource()將會打開一個與flaskr包有關的文件(schema.sql),用處在於當之後部署應用時,你不必知道這個文件的具體位置。
  2. get_db將會返回一個數據庫連接,這被用來執行從文件中讀取的命令。
  3. click.command()定義了一個命令行的命令(init_db),這個命令將會調用init_db()函數,並向用戶展示一個執行成功的信息。

註冊該應用
close_db和init_db_command函數都需要一個應用實例,但是我們所使用的是工廠函數,在寫這個工廠函數時實例是不可以用的。因此,必須要寫一個能夠獲取一個實例並註冊應用的函數。

flaskr/db.py

def init_app(app):
    app.teardown_appcontext(close_db)
    app.cli.add_command(init_db_command)
  1. app.teardown_appcontext()告訴Flaskr在返回響應並且完成清理工作後調用close_db函數
  2. app.cli.add_command() 增加了一個能被flask命令行調用的新函數。

從工廠中導入和調用這個函數。
flaskr/init.py

ef create_app():
    app = ...
    # existing code omitted

    from . import db
    db.init_app(app)

    return app

初始化數據庫文件
現在init_db命令已經在應用中註冊過,使用flask的命令便可以調用它。

如果你仍在之前的頁面運行該服務,你最好重新再一個新的終端中運行它

運行init_db的命令

flask init-db
Initialized the database.

現在在你的項目佈局中應該會有flaskr.sqlite文件出現。

好了,系列二就到這裏,本次主要講了工廠函數的創建以及數據庫連接的建立。
如有錯誤請多指正!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章