python製フレームワークBottleで簡単なWebアプリを作る(その2)

こんにちは。今回のテーマは『【python】Bottleフレームワークで簡単なWebアプリを作る(その2)』です。BottleというシンプルなWebフレームワークを使って簡単なwebアプリを作ってみる企画の2回めです。今回はBottleとは少し離れてしまいますが、SQLAlchemyをつかったデータベースとアプリの連携について書いていきたいと思います。

※本記事はpython製フレームワークBottleで簡単なWebアプリを作る(その1)の続きとして書かれていますので、初めての方はまずコチラをご覧になることをオススメします。

(2018-12-15)加筆修正しました。また、ソース全体が見渡せるようにGitHubにソースコードをUPしました。


【目次】
DBへの登録機能を付与する
DBの準備
SQLAlchemyって何?
SQLAlchemyの導入
モデルの作成
テーブルの作成
登録処理
次回予告

DBへの登録機能を付与する

前回は登録フォームと確認画面への遷移部分を作成し、Bottleのルーティング機能を中心に見てきました。今回はBottleからは少し離れてデータベース周りの処理を行っていきます。なのでBottle特有の処理はあまり登場しません。

DBの準備

まずはデータベースの準備をしましょう。今回はMariaDB(MySQL)を使用します。今回は主題ではないのでmysql自体の操作については簡単に触れるのみにします。

データベースの作成

まずはmysqlにroot権限でログインします。

mysql -u root -p
# パスワード入力してログイン

データベースを作成します。今回はbook_dataという名前で作成します。

[mysql] > cleate database book_data;

ユーザーの作成

book_dataにアクセスするためのユーザーをパスワード付きで作成します。

[mysql] > create user bookuser identified by '{PASSWORD}';

作成したbookuserにbook_dataへの権限を付与します。

[mysql] > grant all on book_data.* to bookuser@localhost identified '{PASSWORD}';

SQLAlchemyって何?

SQLAlchemyはphthon製のORM(Object-relational mapping)です。語弊を恐れずに簡単に言うならばオブジェクトと関連データベースを繋いでくれる役割を果たしてくれるものです。プログラマはORMを利用することでSQLクエリを書くことなく、直感的なオブジェクト操作でCURD操作をすることが出来ます。(まあ、複雑な処理になるとSQLクエリ直接実行することもあるのですが)

SQLAlchemyの導入

SQLAlchemyの導入ですが、今回はpipを使ってインストールします。

pip install sqlalchemy

また、python3でmysqlを使うためにmysqlclientも入れておきます。

pip install mysqlclient

モデルの作成

準備ができましたのでSQLAlchemyを用いてモデルを作成していきます。今回は以下のモデルを作成します。

  • ID: id(int)
  • 書籍名: name(varchar)
  • 巻数: volume(varchar)
  • 著者: author(varchar)
  • 出版社 : publisher(varchar)
  • メモ・感想: memo(text)
  • 作成日時: create_date(datetime)
  • 削除フラグ: del(tinyint)

models.pyを以下のように生成します。(全体のディレクトリ構成はpython製フレームワークBottleで簡単なWebアプリを作る(その1)を参考にしてください)

from sqlalchemy import Column, Integer, String, Text, text, create_engine, DATETIME
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.dialects.mysql import TINYINT
from datetime import datetime

URL = 'mysql://bookuser:{##パスワード##}@localhost/pass_data?charset=utf8'
engin = create_engine(URL, echo=True)

Base = declarative_base()
Session = sessionmaker(bind=engin)
session = Session()


class Books(Base):
    __tablename__ = "books"
    id_ = Column('id', Integer, primary_key = True)
    name = Column('name', String(255))
    volume = Column('volume', String(255))
    author = Column('author', String(255))
    publisher = Column('publisher', String(255))
    memo = Column('memo', Text())
    create_date = Column('create_date', DATETIME, server_default=text('NOW()'), nullable=False)
    delFlg = Column('del', TINYINT(4), server_default=text('0'), nullable=False)

if __name__ == "__main__":
    Base.metadata.create_all(engin)

{##パスワード##}の部分にはbookuser用に作成したパスワードを設定して下さい。SQLAlchemyではデータベースに接続するURLを設定しenginを作成します。データベース毎の違いはここまでで、これ以降は共通の処理で対応出来ます。
declarative_base関数で作成したBaseモデルを継承して各モデルを作成します。また、SQLAlchemyではデータベースで処理を行う際にsessionというオブジェクトで処理を行いますのでsessionも作成しておきます。

※コメントにて指摘頂いた箇所につきソースコード修正しました。(2018/11/12)

テーブルの作成

では、モデルが作成できたので、このモデルを基にデータベースにテーブルを作りましょう。models.pyで

if __name__ == "__main__":
    Base.metadata.create_all(engin)

の箇所はこのために記載しました。models.pyを実行することでモデルをベースにテーブルが作られます。

$ cd [プロジェクトディレクトリ]
$ python3 models.py

これでテーブルが作成されました。試しにmysqlで確かめてみましょう。

$ mysql -u bookuser -p[パスワード] book_data

mysql内で以下のコマンドを実行します。

[mysql] > show tables;

登録処理

モデルの準備とデータベースへのテーブル作成ができましたので、フォームから投稿されたデータをデータベースに登録する処理を書いていきます。

routes.pyの修正

routes.pyにregist関数を追加します。

@app.route('/regist', method='POST')
def regist():

    # データ受取
    name = request.forms.decode().get('name');
    volume = request.forms.decode().get('volume');
    author = request.forms.decode().get('author');
    publisher = request.forms.decode().get('publisher');
    memo = request.forms.decode().get('memo');
    registId = request.forms.decode().get('id')

    if request.forms.get('next') == 'back':
        response.status = 307
        response.set_header("Location", '/add')
        return response
    else:
        if registId is not None:
            # 更新処理
            books = session.query(Books).filter(Books.id_==registId).first()
            books.name = name
            books.volume = volume
            books.author = author
            books.publisher = publisher
            books.memo = memo
            session.commit()
            session.close()
        else:
            # データの保存処理
            books = Books(
                    name = name,
                    volume = volume,
                    author = author,
                    publisher = publisher,
                    memo = memo)
            session.add(books) 
            session.commit()
            session.close()

これでlocalhost:8080/registにデータをPOSTするとデータベースに登録されるようになります。確認画面で「戻る」ボタンが押された場合にはPOSTされる’next’の値が’back’になるためhiddenのinputフォームに登録されている各値をlocalhost:8080/add(登録画面)にPOSTして画面遷移する作りとしました。今回追加した機能はPOSTされたデータをデータベースに追加する処理ですが、将来的には一覧画面からデータの更新処理も行いたいので、registIdの有無で場合分けを行っています。

データの登録処理はモデルを生成してsession.add関数に作成したモデルを入れるだけです。session.commitが行われるまで実行されないので気をつけて下さい。また、処理が終わったらsession.closeすることもお忘れなく。

データの更新処理はsession.query関数にfilterを適用し目的のデータをモデルとして取り出し、そのモデルに修正を加えてcommitすればOKです。この程度の処理でしたらSQLクエリを発行しても苦ではないと思いますが、pythonのモデルとしてデータベース処理が扱える点は便利ですね。

テンプレートファイルの修正

確認画面のテンプレートファイルviews/confirm.htmlにも以下の様に修正を加え、localhost:8080/registに登録データをPOSTするように修正します。

    <form action="" method="POST">

ここの部分を以下のように修正します。

    <form action="regist" method="POST">

次回予告

今のままではデータ保存処理を行った後に、確認画面にとどまってしまいますので今後はデータ保存後は一覧画面に遷移するようにします。一覧画面作成を中心にデータの更新、削除等を行っていく予定です。

最後に

今回も駆け足になってしまいました。全体の完成図や動きが分からないと実感が掴めないと思いますので実際に動くデモサイトの準備をしているところです。次回が最終回の予定です。

記事のためにソースを修正しながら書いた部分でモジュールの読込等に不備がありました。全体が見渡せるようにGitHubにソースコードをUPしました。記事を補完する役割として、ご参考にしていただけると幸いです。

【関連記事】
python製フレームワークBottleで簡単なWebアプリを作る(その1)
【python】Bottleフレームワークで簡単なWebアプリを作る(その3)

Sponsored Link


2件のコメント

  • Python初心者ですが、記事を参考にさせていただいております。
    質問です。
    「モデルを作成する」のところでmodels.pyを実行しようとすると、以下のエラーメッセージが出てしまい全く前に進めません。

    ModuleNotFoundError: No module named ‘config.settings’; ‘config’ is not a package

    様々試してみましたが、全く解決せず…。初期設定か何かで間違っているのでしょうか?
    もしご存知でしたら、ご教示いただけましたら幸いです。

  • YSさん

    コメントに気がつくのが遅れ申し訳ありません。
    もう解決されていると思いますが、返信致します。

    ご指摘のエラーはimportでconfigモジュールを呼んでいるのですが、これが存在しないために起きています。

    これは私の削除し忘れで残っていたもので、本来不必要な行です。

    よって以下行を削除の上、試してみて下さい。

    import config.settings

    記事中のソースも修正しておきます。
    ご連絡ありがとうございました。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です