Python 3 Tutorial 第六堂(1)資料庫介面


除了使用檔案、物件序列化來儲存程式運行結果之外,資料庫的應用也是常見用來儲存資料,以備後續查詢、載入等用途的一個選項,在這邊介紹一下 DBM 與 DB-API 2.0。

DBM

dbm 為柏克萊大學發展的檔案型資料庫,Python 的 dbm 模組提供了對 Unix 程式庫的介面。dbm 物件就像個字典,在不需要關聯式資料庫,只需要快速存取鍵值的場合可以使用,dbm 物件的鍵值都必須是字串。Python 提供 DBM 的多數實現,如果你不確定要用哪一種,可以使用 anydbm 模組,它會檢查並選擇系統上可用的 DBM 實作。

在這邊直接轉貼〈dbm — Interfaces to Unix “databases”〉中的範例程式碼作個示範:

import dbm

# Open database, creating it if necessary.
with dbm.open('cache', 'c') as db:

    # Record some values
    db[b'hello'] = b'there'
    db['www.python.org'] = 'Python Website'
    db['www.cnn.com'] = 'Cable News Network'

    # Note that the keys are considered bytes now.
    assert db[b'www.python.org'] == b'Python Website'
    # Notice how the value is now in bytes.
    assert db['www.cnn.com'] == b'Cable News Network'

    # Often-used methods of the dict interface work too.
    print(db.get('python.org', b'not present'))

    # Storing a non-string key or value will raise an exception (most
    # likely a TypeError).
    db['www.yahoo.com'] = 4

# db is automatically closed when leaving the with statement.

在這邊你看到了 with 這個語法,如註解中說明的,它會自動關閉檔案,現在暫且忽略它,之後我們還會詳細看到 with 的介紹。

DB-API 2.0(PEP 249)

為 Python 中存取資料庫的標準介面,就我的認知而言,其角色應該是類似於 Java 中的 JDBC。Python 中的 sqlite3 模組,提供了 DB-API 2.0 的實作,可用以存取 SQLite 資料庫。接下來的範例,會存取的資料庫表格如下:

資料庫介面

以下直接列出範例程式碼,程式很簡單,應該一目瞭然,API 細節可參考〈sqlite3 — DB-API 2.0 interface for SQLite databases〉。

def connect(name):
    create = not os.path.exists(name)
    conn = sqlite3.connect(name)
    if create:
        cursor = conn.cursor()
        cursor.execute("CREATE TABLE directors ("
            "id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL, "
            "name TEXT UNIQUE NOT NULL)")
        cursor.execute("CREATE TABLE dvds ("
            "id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL, "
            "title TEXT NOT NULL, "
            "year INTEGER NOT NULL, "
            "duration INTEGER NOT NULL, "
            "director_id INTEGER NOT NULL, "
            "FOREIGN KEY (director_id) REFERENCES directors)")
        conn.commit()

    return conn

def add_dvd(conn, title, year, duration, director):
    director_id = get_and_set_director(conn, director)
    cursor = conn.cursor()
    cursor.execute("INSERT INTO dvds "
                   "(title, year, duration, director_id) "
                   "VALUES (?, ?, ?, ?)",
                   (title, year, duration, director_id))
    conn.commit()

def get_and_set_director(conn, director):
    director_id = get_director_id(conn, director)
    if director_id is not None:
        return director_id
    cursor = conn.cursor()
    cursor.execute("INSERT INTO directors (name) VALUES (?)",
                   (director,))
    conn.commit()
    return get_director_id(conn, director)

def get_director_id(conn, director):
    cursor = conn.cursor()
    cursor.execute("SELECT id FROM directors WHERE name=?",
                   (director,))
    fields = cursor.fetchone()
    return fields[0] if fields is not None else None

def all_dvds(conn):
    cursor = conn.cursor()
    sql = ("SELECT dvds.title, dvds.year, dvds.duration, "
           "directors.name FROM dvds, directors "
           "WHERE dvds.director_id = directors.id"
           " ORDER BY dvds.title")
    cursor.execute(sql)
    return [(str(fields[0]), fields[1], fields[2], str(fields[3]))
            for fields in cursor]

def all_directors(conn):
    cursor = conn.cursor()
    cursor.execute("SELECT name FROM directors ORDER BY name")
    return [str(fields[0]) for fields in cursor]

以下是個存取資料庫的例子:

db_name = 'dvd_library.sqlite3'
conn = connect(db_name)
add_dvd(conn, 'Python Tutorial 2013', 2013, 1, 'Justin')
print(all_directors(conn))
print(all_dvds(conn))

練習 8:pickle、shelve 與 DB-API 2.0

在 Lab 檔案中有個 lab/exercises/exercise8,當中有 pickle_ex.py、shelve_ex.py、sql_ex.py 三個檔案,分別是先前介紹 pickleshelve 與 DB-API 2.0 時的三個程式範例,不過程式碼內容不完整,請任選你想要練習的對象,按加上頭列出的範例程式碼,就不齊全的部份補齊。