• トップ
  • ブログ一覧
  • 【第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...

    ライトコードでは、エンジニアを積極採用中!

    ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。

    採用情報へ

    メディアチーム
    メディアチーム
    Show more...

    おすすめ記事

    エンジニア大募集中!

    ライトコードでは、エンジニアを積極採用中です。

    特に、WEBエンジニアとモバイルエンジニアは是非ご応募お待ちしております!

    また、フリーランスエンジニア様も大募集中です。

    background