![見出し画像](https://assets.st-note.com/production/uploads/images/139913136/rectangle_large_type_2_331a0b0fed737b36bc8816356adbe2df.jpeg?width=1200)
リファクタリング的な作業(4)
(Python学習初心者の試行錯誤・備忘録です)
からの続きです。
前回はSELECT文で、条件に合わせた要素数を求める
select_count_where という関数を、Cardsクラス内に作ったところまで。
次はSELECT文で条件・優先順位に応じてカードを一枚取り出す、
select_topcard_whereという関数を考えます。結果は辞書型で取れるようにします
def select_topcard_where(self, condition, *params)->None:
with sqlite3.connect(self.mydb) as con:
con.row_factory = sqlite3.Row # 行を辞書のように扱うための設定
cur = con.cursor()
query = f"SELECT * FROM {self.table} WHERE {condition} LIMIT 1"
cur.execute(query, *params)
row = cur.fetchone()
#rowがNoneのときdict(row)はNoneではなく空の辞書型を返すので
#Noneを返したければ次のようにする必要がある。
if row is not None:
self.topcard = dict(row)
else:
self.topcard = None
テスト
#テストコード
def main():
mycards = Cards()
mycards.select_topcard_where("level = 0")
if mycards.topcard is not None:
print(mycards.topcard)
print(mycards.topcard['face'])
print(mycards.topcard['back'])
else:
print("結果がありません")
mycards.select_topcard_where("level = 1")
if mycards.topcard is not None:
print(mycards.topcard)
else:
print("結果がありません")
if __name__ == "__main__":
main()
テスト結果
{'id': 1, 'face': 'opinion', 'back': '意見', 'level': 0, 'timestamp': '2024-05-08 02:42:38'}
opinion
意見
結果がありません
期待通りに動いています。
UPDATE
データベースの内容更新は、習熟度を示す level要素の値を変えるところで使っています。levelに留まらず汎用的に使えるUPDATE文を用意しておきます。
データ利用のシチュエーションを考えると、一枚のカードtopcardに対してデータを変更するなどして、それを反映させる、という流れになります。
def update_topcard(self, condition, *params)->None:
if self.topcard is not None:
with sqlite3.connect(self.mydb) as con:
cur = con.cursor()
query = f'''UPDATE {self.table} SET {condition}
WHERE id = {self.topcard['id']}'''
cur.execute(query, *params)
con.commit()
テスト
#テストコード
def main():
mycards = Cards()
mycards.select_topcard_where("level = 0")
if mycards.topcard is not None:
print(mycards.topcard)
firstcardid = mycards.topcard['id']
#UPDATE
mycards.update_topcard("level = ?",(1,)) #レベル1に格上げ
mycards.select_topcard_where("id = ?",(firstcardid,)) #どうなったかな?
print(mycards.topcard)
if __name__ == "__main__":
main()
実行結果
{'id': 1, 'face': 'opinion', 'back': '意見', 'level': 0, 'timestamp': '2024-05-08 02:42:38'}
{'id': 1, 'face': 'opinion', 'back': '意見', 'level': 1, 'timestamp': '2024-05-08 02:42:38'}
意図した通り「レベル上げ」ができました。
ここでは、
「覚えた」か「まだ」かによって習熟レベルを設定しなおす関数。
def setlevel(self,new_level):
#self.topcardが有効なら、
if self.topcard is not None:
with sqlite3.connect(self.mydb) as con:
cur = con.cursor()
cur.execute("UPDATE t_cards SET level = ? WHERE id = ?",\
(new_level, self.topcard["id"]))
con.commit()
def inclevel(self):
#self.topcardが有効なら、
if self.topcard is not None:
templevel = self.topcard['level']
if templevel<3:
self.setlevel(templevel+1)
def zerolevel(self):
#self.topcardが有効なら、
if self.topcard is not None:
self.setlevel(0)
最終的に「モデル」の部分は80行ほどになりました。
mvctest_model.py
import sqlite3
import csv
class Cards:
#データベースのt_cardテーブル
def __init__(self, mydb="mydb.sqlite3",table="t_cards") -> None:
self.mydb = mydb
self.table = table
#テーブル t_cards がなければ作成する。
self.createsql = f""" CREATE TABLE IF NOT EXISTS {self.table} (
id INTEGER PRIMARY KEY,
face TEXT NOT NULL,
back TEXT,
level INTEGER DEFAULT 0,
timestamp TEXT DEFAULT CURRENT_TIMESTAMP)
"""
self._create_table()
def _create_table(self):
with sqlite3.connect(self.mydb) as con:
cur = con.cursor()
cur.execute(self.createsql)
con.commit()
def loadfromcsv(self, csvfilename) -> None:
with sqlite3.connect(self.mydb) as con:
cur = con.cursor()
with open(csvfilename, newline='', encoding='utf-8') as csvfile:
csvreader = csv.reader(csvfile, delimiter = ",")
for row in csvreader:
con.execute(f"INSERT INTO {self.table}(face, back) VALUES(?,?)",
(row[0],row[1]))
con.commit()
def loadfromlist(self, datalist:list) -> None:
with sqlite3.connect(self.mydb) as con:
cur = con.cursor()
for row in datalist:
con.execute(f"INSERT INTO {self.table}(face, back) VALUES(?,?)",
(row[0],row[1]))
con.commit()
def cleartable(self)-> None:
with sqlite3.connect(self.mydb) as con:
cur = con.cursor()
cur.execute(f"DELETE FROM {self.table}")
con.commit()
def select_count_where(self,condition, *params):
with sqlite3.connect(self.mydb) as con:
#SELECT COUNT(*)なので返るのは1行、 LIMIT不要。
#結果は必ずあるからNoneの場合分けも不要
cur = con.cursor()
query = f"SELECT COUNT(*) FROM {self.table} WHERE {condition}"
cur.execute(query, *params)
return(cur.fetchone()[0])
def select_topcard_where(self, condition, *params)->None:
with sqlite3.connect(self.mydb) as con:
con.row_factory = sqlite3.Row # 行を辞書のように扱うための設定
cur = con.cursor()
query = f"SELECT * FROM {self.table} WHERE {condition} LIMIT 1"
cur.execute(query, *params)
row = cur.fetchone()
#rowがNoneのときdict(row)はNoneではなく空の辞書型を返すので
#Noneを返したければ次のようにする必要がある。
if row is not None:
self.topcard = dict(row)
else:
self.topcard = None
def update_topcard(self, condition, *params)->None:
if self.topcard is not None:
with sqlite3.connect(self.mydb) as con:
cur = con.cursor()
query = f'''UPDATE {self.table} SET {condition}
WHERE id = {self.topcard['id']}'''
cur.execute(query, *params)
con.commit()
行数は増えてしまうかもだけれど、DB操作の見通しはだいぶ良くなったと思います。次回、このmvctest_model.py を使う形で元のmvctest.pyのコードを書き直してみます。