コンテンツにスキップ

第5章 データベースの基本

1 データベースの基本

データベースを利用するWebサイトを作る前に、db_shouhin.pyにてデータベースの練習を行ってみます。これはweb画面を使わずにコンソールでデータベースの使い方を練習するものです。

今回はSQLiteというpythonにあらかじめ付属するデータベースを使います。VisualStudio CodeにSQLiteのファイルを扱う拡張機能「SQLite3 Editor」をインストールしておきます。コマンドラインで以下から可能です。

code --install-extension yy0931.vscode-sqlite3-editor

もしくはVisualStudio Code画面左の拡張機能のボタンを押して、「SQLite3 Editor」と入力します。

db_shouhin.pyでは、これまでと同様に変数appにFlaskオブジェクトを作成します。

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime

app = Flask(__name__)

次にデータベースを扱う準備をします。ファイル名を指定し、データベースを扱うライブラリ SQLAlchemy のオブジェクト db を作成します。今回は商品や売り上げを管理する販売管理をテーマにし、hanbai.dbというファイルにします。

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///hanbai.db'
db = SQLAlchemy(app)

2 モデルの生成

今回は商品を管理する以下のようなテーブルを作成します。

shouhinテーブル

  • 商品ID sid (整数:Integer) 主キー
  • 商品名 sname (文字列:String 最大255文字)
  • 単価 tanka (整数:Integer)

そのために、商品テーブルのデータを一件入れるためのクラス、Shouhinを作成します。これはSQLAlchemyの「モデル」の作り方に従い作成します。「モデル」とはテーブルの一件のデータを入れる入れ物です。列名を以下のように指定します。

class Shouhin(db.Model):
    sid = db.Column(db.Integer, primary_key=True)
    sname = db.Column(db.String(255))
    tanka = db.Column(db.Integer)

    def __init__(self, sname, tanka):
        self.tanka = tanka
        self.sname = sname

    def __str__(self):
        return f"{self.sid} {self.sname} {self.tanka}"

db.Integerで整数型、db.Stringで文字列型になります。Stringの場合、最大文字数を指定します。また、sidは主キーなので primary_key=True も付加します。

さらにクラス内にコンストラクタを作成しています。インスタンスを生成する際に指定する項目を決めるためです。ここではsnameとtankaを指定します。

str という関数は文字列化するときに自動で呼び出される関数です。printで表示をしやすくしています。

次にテーブルを作成します。これは以下のように行います。まず、テーブルを作成します。これはcreate_all() で可能ですが、その前に drop_all()でいったんテーブルを全削除するようにしています。実行時に毎回、ゼロから作るようにするためです。

with app.app_context():
    db.drop_all() # テーブル全削除
    db.create_all() # テーブル作成

なお、with app.app_context()はFlask-SQLAlchemyをコンソールで動かすために必要な設定です。この後に書くコードは全てこの配下に書いていきます。

ここで実行すると、instanceフォルダにhanbai.dbというファイルが作成されます。これを開くとshouhinテーブルがあるはずdす。

問題

db_uriage.py にて売上テーブルを作成しよう。クラス名はUriageとする。以下の列を作成する、

  • 売上ID uid (整数:Integer) 主キー
  • 商品ID sid (整数:Integer)
  • 販売個数 kosu (整数:Integer)
  • 販売日付 hi (日付:Date)

コンストラクタでは、引数をsidとkosuのみとする。コンストラクタ内で日付(hi)に今日の日付 datetime.today()を入れる

※実行し、hanbai.dbを開き、shouhinのところをuriageに変えるとデータを確認できる。

3 初期化

次に、初期データを設定します。最初にデータを配列に入れておきます。データはShouhinクラスのインスタンスとして生成します。

    # 初期データ追加
    data = [
        Shouhin(sname="りんご", tanka=100),
        Shouhin(sname="みかん", tanka=150),
        Shouhin(sname="いちご", tanka=120),
    ]
    db.session.add_all(data)
    db.session.commit()

その後、db.session.add_all(配列)で全て追加されます。 ただし、db.session.commit() を最後にしないとこの結果が確定されません。データベースを更新(追加、変更、削除)した際にはこれが必要になることを覚えておきましょう。

実行すると、instanceフォルダにhanbai.dbが保存されます。これを開くと、データが入っていることが分かります。

問題

db_uriage.py にて以下のようなデータを初期データとして追加する。

  • 一件目 sid:1 kosu:5
  • 二件目 sid:2 kosu:3
  • 三件目 sid:3 kosu:1
  • 四件目 sid:1 kosu:2

4 全検索

では、設定した全データを表示してみます。わかりやすいように最初にprintで処理内容を表示しています。

Shouhin.query.all() でshouhinテーブルの全ての行を取得します。行は row と言いますので、rowsという変数に格納しています。それをfor文で r に取り出して表示しています。

    print('全検索')
    rows = Shouhin.query.all()
    for r in rows:
        print(r)

問題

db_uriage.py にて売上テーブルの結果を全検索して表示しよう。

5 検索

Shouhin.query.filter(条件)で条件を指定しての検索が可能です。条件式では列名を クラス名.列名 のように書きます。まず、tankaが120以上を抜き出してみます。

    print('検索')
    rows = Shouhin.query.filter(Shouhin.tanka >= 120).all()
    for r in rows:
        print(r)

文字列の検索の場合、containsを使って文字列を含む行を検索できます。

    print('文字列検索')
    rows = Shouhin.query.filter(Shouhin.sname.contains("ご")).all()
    for r in rows:
        print(r)

主キーで検索する場合、get(主キー) または get_or_404(主キー) というメソッドも使うことが出来ます。主キーでの検索は常に一件しか返ってきませんので、rowsではなく、rowに入れます。

    print('主キー検索')
    row = Shouhin.query.get_or_404(2)
    print(row)

get_or_404の場合、存在しない場合には404ページを表示することが出来るので便利です(コンソールでは表示できません)。

問題

db_uriage.py にて売上テーブルでsidが1のデータを検索して全て表示しよう。

6 整列

全検索した結果を単価の昇順で並び替えます。これをqueryの後にorder_by関数を付けます。その引数に、db.asc("列名")、またはdb.desc("列名")を指定します。ascで昇順、descで降順になります。なお、昇順の場合には、db.asc()は省略して、"列名"だけでも可能です。

    print('整列')
    rows = Shouhin.query.order_by(db.desc('tanka')).all()
    for r in rows:
        print(r)

この並び替えは、filterの後にも使用可能です。

問題

db_uriage.py にて売上テーブルをsidの昇順に並び替えて表示しよう。

7 追加

初期化の時にデータを配列に入れて add_all(配列) で追加しましたが、一件のデータであれば、add(データ) で追加できます。

    print('追加')
    s = Shouhin(sname="ぶどう",tanka=300)
    db.session.add(s)
    db.session.commit()

確認のため、全表示してみます。

    # 全表示
    for r in Shouhin.query.all():
        print(r)

問題

db_uriage.py にて売上テーブルに sid=2,kosu=1のデータを追加し、全検索しして表示しよう。

8 変更

既存データを変更するには、まず、get_or_404(主キー)を使って指定した主キーのデータを取得します。そして、属性を書き換えます。最後にcommitします。

    print('変更')
    row = Shouhin.query.get_or_404(4)
    row.sname = "なし"
    row.tanka = 200
    db.session.commit()

確認のため、全表示してみます。

    # 全表示
    for r in Shouhin.query.all():
        print(r)

問題

db_uriage.py にて売上テーブルの主キー uidが5のデータを sid=3,kosu=2に変更し、全検索しして表示しよう。

9 削除

既存データを削除するには、まず、get_or_404(主キー)を使って指定した主キーのデータを取得します。そして、db.session.delete(データ) とすれば削除されます。最後にcommitします。

    print('削除')
    row = Shouhin.query.get_or_404(4)
    db.session.delete(row)
    db.session.commit()

確認のため、全表示してみます。

    # 全表示
    for r in Shouhin.query.all():
        print(r)

問題

db_uriage.py にて売上テーブルのuidが5のデータを削除しよう。