【Python】SQLite データベースを読み取り専用で開くコード例【ReadOnly】

Python の sqlite3 でデータベースを開くときに、『読み取り専用 (ReadOnly)』で開くコード例です。

Python のマニュアルに『読み出し専用モード』で使用する方法が載っていました。

sqlite3.connect(database[, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements, uri])

説明によると、『引数ひきすうuri=True を指定すると、ファイルの指定方法が変わって、読み出し専用モード ('mode=ro') などのオプションが使えるようになる』とのことでした。

引数の uri は、Python 3.4 で追加された、とのことでした。

'mode=ro' を使用したことで、実際にデータベースへの『書き込み処理』を禁止することができました。

INSERT とか REPLACE とか、書き込みをするような SQL 文を実行したら、例外 sqlite3.OperationalError が出るようになりました。

sqlite3.OperationalError: attempt to write a readonly database

『リードオンリーのデータベースに、書き込みをしようとしました。』というエラーですね。

意図した通りです。

データベースの変更を防ぐことができました。

自分は、データベースを更新するプログラムと、分析を行うプログラムを分けて書くのが好きです。

データ分析をするときは、書き込みをする必要なかったので、『読み取り専用で SQLite DB を開きたいな』と思ったんですよね。

これで、データベースへの接続が、より安心してできるようになりました。

『Python と SQLite のマニュアルの場所』と、『具体的なコード例』を紹介します。

Python マニュアルの場所

sqlite3 モジュール

コネクト sqlite3.connect(database[, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements, uri])

SQLite データベースへの接続を開くメソッド。引数に uri=True を指定したら、db ファイルの指定方法が変わって、『読み出し専用モード ('mode=ro')』などのオプションが使えるようになりました。

カーソル sqlite3.Connection.cursor(factory=Cursor)

エクセキュート sqlite3.Cursor.execute(sql[, parameters])

コミット sqlite3.Connection.commit()

クローズ sqlite3.Connection.close()

エクセプション エスキューライトスリー ドット エラー exception sqlite3.Error

traceback モジュール

フォーマットエクセ traceback.format_exc(limit=None, chain=True)

組み込み機能

SQLite マニュアルの場所

SQLite Home Page > Alphabetical List Of SQLite Documents

URI ユーアールアイ(ユニフォーム リソース アイデンティファイアス)Uniform Resource Identifiers

ここに、『URI のファイル名を使うと、"mode=ro" とか、色々なオプションが使えるようになります。』といった内容の説明がありました。

クリエートテーブル CREATE TABLE

セレクト SELECT

リプレース REPLACE

"REPLACE""INSERT OR REPLACE" の別名で、機能の説明は The ON CONFLICT Clause のページにありました。

コード例

データベースを作ってから、『読み出し専用モード ('mode=ro')』で開くコード例です。

読み出し専用モードの時に『データの書き込み』を実行したら、意図した通り、

sqlite3.OperationalError: attempt to write a readonly database

というエラーが発生しました。

"""read only で SQLite DB を開く Python コード例"""

import sqlite3
import traceback


def main():

    # 【まずはデータベースを作ります】
    # (1/15) 何かデータを用意する
    src_datas = [
        ('101', 'あああ'),
        ('102', 'いいい'),
        ('103', 'ううう'),
        ('104', 'えええ'),
        ('105', 'おおお'),
    ]

    # (2/15) ファイル名を決める
    db_file = r'F:\project\data\sample.db'

    # (3/15) データベースに接続する
    connect = sqlite3.connect(db_file)

    try:
        # (4/15) カーソルを取得する
        c = connect.cursor()

        # (5/15) テーブルが無ければ作る
        c.execute("""CREATE TABLE IF NOT EXISTS names (
            data_id INTEGER PRIMARY KEY,
            data_name TEXT)""")

        # (6/15) データベースに反映する
        connect.commit()

        # (7/15) データを追加する
        for src_columns in src_datas:
            c.execute('REPLACE INTO names VALUES (?,?)', src_columns)

        # (8/15) データベースに反映する
        connect.commit()

    finally:
        # (9/15) データベースを閉じる
        connect.close()


    # 【読み取り専用で開きます】
    # (10/15) 読み取り専用でデータベースに接続する (ro: read only)
    ro_connect = sqlite3.connect(f'file:{db_file}?mode=ro', uri=True)

    print('(10/15) 読み取り専用で開く')
    print(f"sqlite3.connect('file:{db_file}?mode=ro', uri=True)")

    try:
        # (11/15) カーソルを取得する
        ro_c = ro_connect.cursor()

        # (12/15) データを選ぶ
        ro_c.execute('SELECT * FROM names')

        # (13/15) 試しにすべてのデータを読み込む
        print('\n(13/15) データは読み込める')
        datas = ro_c.fetchall()
        for columns in datas:
            print(columns)

        print('\n(14/15) わざと『データの書き込み』を試みる')
        ro_c.execute('REPLACE INTO names VALUES (?,?)', ('901', 'わわわ'))

    except sqlite3.Error:
        print('【エラーの内容】')
        print(traceback.format_exc())

    finally:
        print('(15/15) データベースを閉じる')
        ro_connect.close()

    print('\n(以上です)')
    return


if __name__ == '__main__':
    main()

実行結果

(10/15) 読み取り専用で開く
sqlite3.connect('file:F:\project\data\sample.db?mode=ro', uri=True)

(13/15) データは読み込める
(101, 'あああ')
(102, 'いいい')
(103, 'ううう')
(104, 'えええ')
(105, 'おおお')

(14/15) わざと『データの書き込み』を試みる
【エラーの内容】
Traceback (most recent call last):
  File "f:/*****/main.py", line 70, in main
    ro_c.execute('REPLACE INTO names VALUES (?,?)', ('901', 'わわわ'))
sqlite3.OperationalError: attempt to write a readonly database

(15/15) データベースを閉じる

(以上です)
タイトルとURLをコピーしました