• トップ
  • ブログ一覧
  • 【第2回】FastAPIチュートリアル: ToDoアプリを作ってみよう【モデル構築編】
  • 【第2回】FastAPIチュートリアル: ToDoアプリを作ってみよう【モデル構築編】

    広告メディア事業部広告メディア事業部
    2019.11.21

    IT技術

    FastAPIチュートリアル: toDoアプリを作ってみよう~第2回~

    前回の記事「【第1回】FastAPIチュートリアル: ToDoアプリを作ってみよう【環境構築編】」の続きです。

    今回も、ToDoアプリを作る過程で、FastAPIの使い方を学んでいきましょう!

    第1回はこちら

    featureImg2019.11.14【第1回】FastAPIチュートリアル: ToDoアプリを作ってみよう【環境構築編】FastAPIチュートリアル: toDoアプリを作ってみるFastAPI は、最近注目を集めている WebAPIフレー...

    情報の管理

    今回のToDoアプリケーションでは、ユーザ情報と各ユーザのタスクを管理する必要があります。

    これらはモデルを定義し、データベースに格納しておくことで、コントローラ側とデータのやりとりをできるようにしておく必要があります。

    本チュートリアルでは、Pythonのデータベース管理ライブラリであるSQLAlchemyを用います。

    インストール

    初めて使う方はインストールしましょう。

    1$ pip install sqlalchemy

    データベースを作成する

    モデルを構築する前に、まずはデータベースを作成しましょう!

    新しくデータベースを定義するファイル、db.py を作成してください。

    今回は、SQLite3 をデータベースとして使うことにします。

    db.py

    db.pyには、以下の内容を記述してください。

    1from sqlalchemy.ext.declarative import declarative_base
    2from sqlalchemy import create_engine
    3from sqlalchemy.orm import sessionmaker
    4
    5Base = declarative_base()
    6RDB_PATH = 'sqlite:///db.sqlite3'
    7ECHO_LOG = True
    8
    9engine = create_engine(
    10   RDB_PATH, echo=ECHO_LOG
    11)
    12
    13Session = sessionmaker(bind=engine)
    14session = Session()

    ここでは、SQLite3 のベースとセッションを定義しています。

    今後、データベースをいじる際には、このファイルをインポートしセッションを行います。

    この辺りは後々説明しますので、今の段階では「SQLite3っていうデータベースを使うんだな」程度の理解で大丈夫です。

    モデル構築

    それでは、早速モデルを構築していきましょう。

    新しく、models.py というファイルを作成します。

    このファイルに、モデルの構成(データベース構成)を SQLAlchemy を用いて記述していきます。

    コードを書く前に、作りたいモデルを整理しましょう

    モデル例

    例えば、以下のようなモデルが考えられます。

    Userid (各ユーザ固有のID: 主キー)
    username (ログインに必要なユーザネーム)
    password (ログインに必要なパスワード)
    mail (メールアドレス)
    Taskid (各タスク固有のID: 主キー)
    user_id (紐づくユーザID: 外部キー)
    content (タスク内容)
    deadline (タスクの締め切り)
    date (タスク作成日)
    done (タスクが終了したか)

    少なくとも上記のような情報を持ったモデルがあれば、「ToDo」アプリは作成出来そうです。

    早速コードに落としてみましょう!

    実装してみる (models.py)

    モデルの定義を models.py にしていきます。

    内容は、先ほどの表を参考にして、SQLAlchemy の文法に従って書くと、以下のようになります。

    1from datetime import datetime
    2
    3from db import Base
    4
    5from sqlalchemy import Column, String, DateTime, ForeignKey
    6from sqlalchemy.sql.functions import current_timestamp
    7from sqlalchemy.dialects.mysql import INTEGER, BOOLEAN
    8
    9import hashlib
    10
    11SQLITE3_NAME = "./db.sqlite3"
    12
    13
    14class User(Base):
    15    """
    16    Userテーブル
    17
    18    id       : 主キー
    19    username : ユーザネーム
    20    password : パスワード
    21    mail     : メールアドレス
    22    """
    23    __tablename__ = 'user'
    24    id = Column(
    25        'id',
    26        INTEGER(unsigned=True),
    27        primary_key=True,
    28        autoincrement=True,
    29    )
    30    username = Column('username', String(256))
    31    password = Column('password', String(256))
    32    mail = Column('mail', String(256))
    33
    34    def __init__(self, username, password, mail):
    35        self.username = username
    36        # パスワードはハッシュ化して保存
    37        self.password = hashlib.md5(password.encode()).hexdigest()
    38        self.mail = mail
    39
    40    def __str__(self):
    41        return str(self.id) + ':' + self.username
    42
    43
    44class Task(Base):
    45    """
    46    toDoタスク
    47
    48    id       : 主キー
    49    user_id  : 外部キー
    50    content  : 内容
    51    deadline : 締め切り
    52    date     : 作成日
    53    done     : タスクを終了したか
    54    """
    55    __tablename__ = 'task'
    56    id = Column(
    57        'id',
    58        INTEGER(unsigned=True),
    59        primary_key=True,
    60        autoincrement=True,
    61    )
    62
    63    user_id = Column('user_id', ForeignKey('user.id'))
    64    content = Column('content', String(256))
    65    deadline = Column(
    66        'deadline',
    67        DateTime,
    68        default=None,
    69        nullable=False,
    70    )
    71    date = Column(
    72        'date',
    73        DateTime,
    74        default=datetime.now(),
    75        nullable=False,
    76        server_default=current_timestamp(),
    77    )
    78    done = Column('done', BOOLEAN, default=False, nullable=False)
    79
    80    def __init__(self, user_id: int, content: str, deadline: datetime, date: datetime = datetime.now()):
    81        self.user_id = user_id
    82        self.content = content
    83        self.deadline = deadline
    84        self.date = date
    85        self.done = False
    86
    87    def __str__(self):
    88        return str(self.id) + \
    89               ': user_id -> ' + str(self.user_id) + \
    90               ', content -> ' + self.content + \
    91               ', deadline -> ' + self.deadline.strftime('%Y/%m/%d - %H:%M:%S') + \
    92               ', date -> ' + self.date.strftime('%Y/%m/%d - %H:%M:%S') + \
    93               ', done -> ' + str(self.done)

    必要はないのですが、デバッグ用でdef __str__(self) を定義しておくと便利です。

    (print(Model) で表示したいものを書く)

    これでモデルの定義が終わりました。

    コードは、ひとつひとつ丁寧に追えば「なるほど、SQLAlchemyではこう書くのか」と理解できると思います。

    データベースの作成とサンプルデータの挿入

    それでは、データベースを実際に作成しましょう。

    また、ついでに試しのデータを格納してみます。

    新しく create_table.py を作り、以下のコードを書いてください。

    1# create_table.py
    2from models import *
    3import db
    4import os
    5
    6
    7if __name__ == "__main__":
    8    path = SQLITE3_NAME
    9    if not os.path.isfile(path):
    10
    11        # テーブルを作成する
    12        Base.metadata.create_all(db.engine)
    13
    14    # サンプルユーザ(admin)を作成
    15    admin = User(username='admin', password='fastapi', mail='hoge@example.com')
    16    db.session.add(admin)  # 追加
    17    db.session.commit()  # データベースにコミット
    18
    19    # サンプルタスク
    20    task = Task(
    21        user_id=admin.id,
    22        content='〇〇の締め切り',
    23        deadline=datetime(2019, 12, 25, 12, 00, 00),
    24    )
    25    print(task)
    26    db.session.add(task)
    27    db.session.commit()
    28
    29    db.session.close()  # セッションを閉じる

    では、このファイルを実行してみましょう。

    1$ python create_table.py

    実行すると、db.sqlite3というファイルが作成されます。

    確認

    データが格納できているかも確認してみましょう!

    1$ sqlite3 db.sqlite3
    2
    3sqlite> .table
    4task  user
    5sqlite> select * from user;
    61|admin|0d32bced91aa5c2ee5696fc7995370ae|hoge@example.com
    7sqlite> select * from task;
    81|1|〇〇の締め切り|2019-12-25 12:00:00.000000|2019-10-08 17:43:31.321445|0

    このように表示されればOKです!

    パスワードもしっかり暗号化されていますね。

    これで、「データベースの作成」および「モデルの作成」、そして「サンプルデータの追加」ができました。

    では、コントローラやビューと連携させてみましょう

    モデルの情報を管理者ページに反映させる

    前回作った管理者ページに、モデル情報を反映させてみます。

    まだ、「ログイン機能」などはないので、先ほど作ったadminユーザのみ対応させてみましょう!

    controllers.pyを修正

    まず、以下の内容を追加でインポートします。

    1import db
    2from models import User, Task

    次に、admin() を修正していきます。

    現段階では動作確認なので、adminユーザのみを取得し、それに紐づいたタスクをデータベースから取得します。

    また、データを取得した後は、セッションをクローズするのを忘れないようにしましょう。

    1def admin(request: Request):
    2    # ユーザとタスクを取得
    3    # とりあえず今はadminユーザのみ取得
    4    user = db.session.query(User).filter(User.username == 'admin').first()
    5    task = db.session.query(Task).filter(Task.user_id == user.id).all()
    6    db.session.close()
    7
    8    return templates.TemplateResponse('admin.html',
    9                                      {'request': request,
    10                                       'user': user,
    11                                       'task': task})

    ビュー templates/admin.htmlを修正

    次に、ビューを修正します。

    取得したタスクは、テーブルとして表示するようにしてみました。

    1{% extends "layout.html" %}
    2{% block content %}
    3<br>
    4<h2>Administrator page</h2>
    5<hr>
    6<p>Hi, {{ user['username'] }}.</p>
    7
    8<br>
    9<table class="table">
    10    <thead class="table-dark">
    11    <tr>
    12        <th>#</th>
    13        <th>内容</th>
    14        <th>締め切り</th>
    15        <th>掲載日</th>
    16        <th>終了</th>
    17    </tr>
    18    </thead>
    19    <tbody>
    20    {% for t in task %}
    21    <tr>
    22        <td>{{ t['id'] }}</td>
    23        <td>{{ t['content'] }}</td>
    24        <td>{{ t['deadline'] }}</td>
    25        <td>{{ t['date'] }}</td>
    26        <td>{% if t['done'] %}
    27            <div class="text-success"></div>
    28            {% else %}
    29            <div class="text-danger"></div>
    30            {% endif %}
    31        </td>
    32    </tr>
    33    {% endfor %}
    34    </tbody>
    35</table>
    36
    37{% endblock %}

    確認

    では、http://127.0.0.1:8000/admin にアクセスしてみましょう。

    改良したadminページ

    うまくモデルからデータを取得できていそうです!

    第3回へつづく!

    今回は、モデルの構築をメインに行いました。

    ただ、まだまだ「ToDoアプリ」とは言えませんね。

    「ログイン機能」はない上に、ビューからToDoリストに「タスク追加」もできません。

    次回は、そのあたりを中心に解説・実装していきたいと思います!

    第3回の記事はこちら

    featureImg2019.11.18【第3回】FastAPIチュートリアル: toDoアプリを作ってみよう【認証・ユーザ登録編】FastAPIチュートリアル: toDoアプリを作ってみよう~第3回~前回の記事「【第2回】FastAPIチュートリア...

    ResponderとFastAPIを実際に使って比較してみた

    featureImg2020.01.10ResponderとFastAPIを実際に使って比較してみたResponderとFastAPIを比較したい!Webアプリケーションといえば、PHPの「CakePHP」、Pytho...

    こちらの記事もオススメ!

    featureImg2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...

    featureImg2020.07.30Python 特集実装編※最新記事順Responder + Firestore でモダンかつサーバーレスなブログシステムを作ってみた!P...

    広告メディア事業部

    広告メディア事業部

    おすすめ記事