National Central University
Uedu Main Site
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try an AI Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

3

Local AI

--

Uedu Code

--

中央大學 AQI 80 24°C PM2.5 21
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 09

接上資料庫

sqlite3、每個請求一條連線、參數化查詢
Interaction實作 30 minute(s)

Learning Objectives

  • 說明為什麼資料不能只放在 Python 的串列裡
  • 使用 sqlite3 的 connect、execute、fetchone、fetchall、commit 與 row_factory
  • 寫出每個請求一條連線的 get_db,並用 teardown_appcontext 關閉
  • 用 schema.sql 與 executescript 建立資料表
  • 用參數化查詢把使用者輸入交給資料庫
  • 判斷什麼情況 SQLite 夠用、什麼情況要換成 MySQL 這類資料庫

資料放在串列裡有什麼問題

前幾課的留言都放在 Python 的串列裡。串列存在記憶體,程式一結束就消失:重新啟動、更新程式、伺服器當機,資料全部不見。正式環境常常同時開好幾個程序處理請求,每個程序各有一份串列,內容還會不一致。

資料庫把資料存在磁碟上,並且處理好多人同時讀寫。這一課用 SQLite:它不需要另外安裝伺服器,整個資料庫就是一個檔案,Python 也內建了 sqlite3 模組。

如果還不熟 SQL,這一課用到的只有 CREATE TABLE、INSERT、SELECT,看範例就能理解,詳細語法在 SQL 軌。想直接打開資料庫檔案查看內容,可以用 SQLiteStudio、DB Browser for SQLite 這類工具。

逐步看:sqlite3 的基本操作

這個範例還沒有用到 Flask。請注意 commit 出現的位置,以及 fetchone、fetchall 各回傳什麼。

逐步看:每個請求一條連線

網站會同時處理很多請求,所以每個請求各開一條連線,處理完就關掉。請看「開啟連線」與「關閉連線」什麼時候印出來,以及 /hello 為什麼一次都沒有印。

這段寫法後面每一課都會用到

def get_db():
    if 'db' not in g:
        g.db = sqlite3.connect('site.db')
        g.db.row_factory = sqlite3.Row
    return g.db

@app.teardown_appcontext
def close_db(exc):
    db = g.pop('db', None)
    if db is not None:
        db.close()

為什麼不在程式開頭開一條全域連線,大家共用?因為同時進來的請求會互相干擾:一個請求還沒做完,另一個就可能把它的修改一起 commit 或還原掉。

第 5 課提過 g 只活在一次請求,這裡正好用上。teardown_appcontext 的函數在錯誤發生時也會執行,連線一定會被關閉。

逐步看:用 schema.sql 建立資料表

把建立資料表的 SQL 放在獨立的 schema.sql(在輸出區的檔案分頁),程式啟動時執行一次。這段程式執行完,請到檔案分頁確認 schema.sql 的內容。

參數化查詢

把使用者輸入放進 SQL 只有一種正確的寫法:

# 正確:問號+參數
db.execute('SELECT * FROM notes WHERE id = ?', (note_id,))

# 有問題的寫法:把值直接接進字串
db.execute(f'SELECT * FROM notes WHERE id = {note_id}')

用字串接起來的話,輸入裡的引號會被當成 SQL 的一部分。輕則像下面的練習一樣直接出錯,重則被人改變查詢的意思,這叫 SQL 注入,第 17 課會實際操作。

參數化查詢把 SQL 和值分開送給資料庫,值再怎麼寫都只會被當成值。? 只能放值,不能放表名和欄位名。

資料庫面板看到的資料,和用 SQLiteStudio 開檔案看到的是同一份。

互動練習:比較串列與資料庫

這個筆記網站一開始把資料放在串列,get_db 和資料表已經準備好,但還沒有被使用。

「存檔並重新啟動」等於真的重新啟動伺服器,記憶體裡的東西會全部清空,磁碟上的檔案會留著。沒有改程式也可以按。按完之後再按瀏覽器的 ↻,任務才會判定。

互動練習:查詢一筆資料與搜尋

這個版本已經用資料庫了,資料表裡先放了一則「第一週筆記」。note 還沒寫完,search 用了有問題的寫法,等一下要親手改掉。

改完記得「存檔並重新啟動」,再操作一次。伺服器出錯時,錯誤會顯示在「伺服器紀錄」面板。

動手做:新增與查詢筆記

get_db 和資料表都寫好了。完成兩個 view 函數(程式最下面的測試不要改):

  1. add_note:用參數化查詢寫入一筆,確認寫入,回傳新資料的編號(字串)
  2. get_note:用參數化查詢找出這一筆,找不到時回 404,找到時回傳標題
你的輸出
預期輸出

SQLite 夠用嗎

SQLite 官方的說法是:多數流量中低的網站,SQLite 都很合適。它的限制在於同一時間只有一個程式能寫入,而且資料庫要和程式放在同一台機器上。

情況 選擇
課堂作業、個人專案、小型網站 SQLite
資料和程式在同一台機器,寫入不頻繁 SQLite
很多使用者同時大量寫入 MySQL、PostgreSQL
好幾台伺服器要連同一個資料庫 MySQL、PostgreSQL
需要不同帳號各有不同權限 MySQL、PostgreSQL

SQL 語法大多相通,要換時改的主要是連線方式、參數符號(MySQL 的 pymysql 用 %s)與部分資料型別。MySQL 的用法可以到 SQL 軌學習。

想一想

1.

下面的程式會印出什麼?

import sqlite3
con = sqlite3.connect("site.db")
con.execute("CREATE TABLE t (x)")
con.execute("INSERT INTO t VALUES (1)")
con.close()
con = sqlite3.connect("site.db")
print(con.execute("SELECT COUNT(*) FROM t").fetchone()[0])
2.

下面的程式會丟出什麼例外?

import sqlite3
con = sqlite3.connect(":memory:")
con.execute("CREATE TABLE t (id INTEGER)")
con.execute("SELECT * FROM t WHERE id = ?", (5))
3.

下面的程式會印出什麼?

import sqlite3
con = sqlite3.connect(":memory:")
con.execute("CREATE TABLE t (id INTEGER)")
print(con.execute("SELECT * FROM t WHERE id = ?", (5,)).fetchone())
4.

為什麼連線要放在 g 裡,而不是開一條全域變數讓所有請求共用?

5.

下面哪一種情況,最需要從 SQLite 換成 MySQL 這類資料庫?

第 1 次作答

重點整理

  • 串列在記憶體,重新啟動就不見;資料庫在磁碟
  • connect → execute → fetchone/fetchall;有修改要 commit;row_factory = sqlite3.Row 才能用欄位名稱取值
  • 每個請求一條連線:get_db 放進 g,teardown_appcontext 負責關閉
  • schema.sql+executescript 建立資料表,寫 IF NOT EXISTS 才能重複執行
  • 使用者輸入一律用 ? 參數化,只有一個值時 tuple 要寫逗號
  • SQLite 適合單機、寫入不多的網站;多台伺服器、大量同時寫入時換 MySQL 這類資料庫

下一課用這些工具做出完整的留言板:新增、查詢、修改、刪除,以及出錯時怎麼處理。

這一課完成了!
Next lesson:做出新增、查詢、修改、刪除
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。