Python の sqlite3 でデータベースを開くときに、『読み取り専用 (ReadOnly)』で開くコード例です。
Python のマニュアルに『読み出し専用モード』で使用する方法が載っていました。
説明によると、『引数に 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 モジュール
SQLite データベースへの接続を開くメソッド。引数に uri=True
を指定したら、db ファイルの指定方法が変わって、『読み出し専用モード ('mode=ro'
)』などのオプションが使えるようになりました。
(Python) sqlite3.Connection.cursor(factory=Cursor)
(Python) sqlite3.Cursor.close()
(Python) sqlite3.Cursor.execute(sql[, parameters])
(Python) sqlite3.Connection.commit()
(Python) sqlite3.Connection.close()
(Python) exception sqlite3.Error
traceback モジュール
(Python) traceback.format_exc(limit=None, chain=True)
組み込み機能
raw strings r''
の説明(引用符の前に『アール r 』を付けた文字列)
f-string f''
の説明(引用符の前に『エフ f 』を付けた文字列、フォーマット済み文字列リテラル)
SQLite マニュアルの場所
SQLite Home Page > Alphabetical List Of SQLite Documents
(SQLite) Uniform Resource Identifiers
URI ユーアールアイ(ユニフォーム リソース アイデンティファイアス)
ここに、『URI のファイル名を使うと、"mode=ro"
とか、色々なオプションが使えるようになります。』といった内容の説明がありました。
(SQLite) CREATE TABLE
クリエートテーブル
(SQLite) SELECT
セレクト
(SQLite) REPLACE
リプレース
"REPLACE"
は "INSERT OR REPLACE"
の別名で、機能の説明は
(SQLite) 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) データベースに接続する
conn = sqlite3.connect(db_file)
# (4/15) カーソルを取得する
c = conn.cursor()
try:
# (5/15) テーブルが無ければ作る
c.execute("""CREATE TABLE IF NOT EXISTS names (
data_id INTEGER PRIMARY KEY,
data_name TEXT)""")
# (6/15) データベースに反映する
conn.commit()
# (7/15) データを追加する
for src_columns in src_datas:
c.execute('REPLACE INTO names VALUES (?,?)', src_columns)
# (8/15) データベースに反映する
conn.commit()
finally:
# (9/15) データベースを閉じる
c.close()
conn.close()
# 【読み取り専用で開きます】
# (10/15) 読み取り専用でデータベースに接続する (ro: read only)
ro_conn = sqlite3.connect(f'file:{db_file}?mode=ro', uri=True)
print('(10/15) 読み取り専用で開く')
print(f"sqlite3.connect('file:{db_file}?mode=ro', uri=True)")
# (11/15) カーソルを取得する
ro_c = ro_conn.cursor()
try:
# (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_c.close()
ro_conn.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 69, in main
ro_c.execute('REPLACE INTO names VALUES (?,?)', ('901', 'わわわ'))
sqlite3.OperationalError: attempt to write a readonly database
(15/15) データベースを閉じる
(以上です)