見出し画像

リファクタリング的な作業(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のコードを書き直してみます。

この記事が気に入ったらサポートをしてみませんか?