
【第2回】FastAPIチュートリアル: ToDoアプリを作ってみよう【モデル構築編】
2021.12.20
FastAPIチュートリアル: toDoアプリを作ってみよう~第2回~
前回の記事「【第1回】FastAPIチュートリアル: ToDoアプリを作ってみよう【環境構築編】」の続きです。
今回も、ToDoアプリを作る過程で、FastAPIの使い方を学んでいきましょう!
第1回はこちら
情報の管理
今回のToDoアプリケーションでは、ユーザ情報と各ユーザのタスクを管理する必要があります。
これらはモデルを定義し、データベースに格納しておくことで、コントローラ側とデータのやりとりをできるようにしておく必要があります。
本チュートリアルでは、Pythonのデータベース管理ライブラリであるSQLAlchemyを用います。
インストール
初めて使う方はインストールしましょう。
1 | $ pip install sqlalchemy |
データベースを作成する
モデルを構築する前に、まずはデータベースを作成しましょう!
新しくデータベースを定義するファイル、db.py を作成してください。
今回は、SQLite3 をデータベースとして使うことにします。
db.py
db.pyには、以下の内容を記述してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker Base = declarative_base() RDB_PATH = 'sqlite:///db.sqlite3' ECHO_LOG = True engine = create_engine( RDB_PATH, echo=ECHO_LOG ) Session = sessionmaker(bind=engine) session = Session() |
ここでは、SQLite3 のベースとセッションを定義しています。
今後、データベースをいじる際には、このファイルをインポートしセッションを行います。
この辺りは後々説明しますので、今の段階では「SQLite3っていうデータベースを使うんだな」程度の理解で大丈夫です。
モデル構築
それでは、早速モデルを構築していきましょう。
新しく、models.py というファイルを作成します。
このファイルに、モデルの構成(データベース構成)を SQLAlchemy を用いて記述していきます。
コードを書く前に、作りたいモデルを整理しましょう。
モデル例
例えば、以下のようなモデルが考えられます。
User | id (各ユーザ固有のID: 主キー) username (ログインに必要なユーザネーム) password (ログインに必要なパスワード) mail (メールアドレス) |
Task | id (各タスク固有のID: 主キー) user_id (紐づくユーザID: 外部キー) content (タスク内容) deadline (タスクの締め切り) date (タスク作成日) done (タスクが終了したか) |
少なくとも上記のような情報を持ったモデルがあれば、「ToDo」アプリは作成出来そうです。
早速コードに落としてみましょう!
実装してみる (models.py)
モデルの定義を models.py にしていきます。
内容は、先ほどの表を参考にして、SQLAlchemy の文法に従って書くと、以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | from datetime import datetime from db import Base from sqlalchemy import Column, String, DateTime, ForeignKey from sqlalchemy.sql.functions import current_timestamp from sqlalchemy.dialects.mysql import INTEGER, BOOLEAN import hashlib SQLITE3_NAME = "./db.sqlite3" class User(Base): """ Userテーブル id : 主キー username : ユーザネーム password : パスワード mail : メールアドレス """ __tablename__ = 'user' id = Column( 'id', INTEGER(unsigned=True), primary_key=True, autoincrement=True, ) username = Column('username', String(256)) password = Column('password', String(256)) mail = Column('mail', String(256)) def __init__(self, username, password, mail): self.username = username # パスワードはハッシュ化して保存 self.password = hashlib.md5(password.encode()).hexdigest() self.mail = mail def __str__(self): return str(self.id) + ':' + self.username class Task(Base): """ toDoタスク id : 主キー user_id : 外部キー content : 内容 deadline : 締め切り date : 作成日 done : タスクを終了したか """ __tablename__ = 'task' id = Column( 'id', INTEGER(unsigned=True), primary_key=True, autoincrement=True, ) user_id = Column('user_id', ForeignKey('user.id')) content = Column('content', String(256)) deadline = Column( 'deadline', DateTime, default=None, nullable=False, ) date = Column( 'date', DateTime, default=datetime.now(), nullable=False, server_default=current_timestamp(), ) done = Column('done', BOOLEAN, default=False, nullable=False) def __init__(self, user_id: int, content: str, deadline: datetime, date: datetime = datetime.now()): self.user_id = user_id self.content = content self.deadline = deadline self.date = date self.done = False def __str__(self): return str(self.id) + \ ': user_id -> ' + str(self.user_id) + \ ', content -> ' + self.content + \ ', deadline -> ' + self.deadline.strftime('%Y/%m/%d - %H:%M:%S') + \ ', date -> ' + self.date.strftime('%Y/%m/%d - %H:%M:%S') + \ ', done -> ' + str(self.done) |
必要はないのですが、デバッグ用で def __str__(self) を定義しておくと便利です。
( print(Model) で表示したいものを書く)
これでモデルの定義が終わりました。
コードは、ひとつひとつ丁寧に追えば「なるほど、SQLAlchemyではこう書くのか」と理解できると思います。
データベースの作成とサンプルデータの挿入
それでは、データベースを実際に作成しましょう。
また、ついでに試しのデータを格納してみます。
新しく create_table.py を作り、以下のコードを書いてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | # create_table.py from models import * import db import os if __name__ == "__main__": path = SQLITE3_NAME if not os.path.isfile(path): # テーブルを作成する Base.metadata.create_all(db.engine) # サンプルユーザ(admin)を作成 admin = User(username='admin', password='fastapi', mail='hoge@example.com') db.session.add(admin) # 追加 db.session.commit() # データベースにコミット # サンプルタスク task = Task( user_id=admin.id, content='〇〇の締め切り', deadline=datetime(2019, 12, 25, 12, 00, 00), ) print(task) db.session.add(task) db.session.commit() db.session.close() # セッションを閉じる |
では、このファイルを実行してみましょう。
1 | $ python create_table.py |
実行すると、db.sqlite3というファイルが作成されます。
確認
データが格納できているかも確認してみましょう!
1 2 3 4 5 6 7 8 | $ sqlite3 db.sqlite3 sqlite> .table task user sqlite> select * from user; 1|admin|0d32bced91aa5c2ee5696fc7995370ae|hoge@example.com sqlite> select * from task; 1|1|〇〇の締め切り|2019-12-25 12:00:00.000000|2019-10-08 17:43:31.321445|0 |
このように表示されればOKです!
パスワードもしっかり暗号化されていますね。
これで、「データベースの作成」および「モデルの作成」、そして「サンプルデータの追加」ができました。
では、コントローラやビューと連携させてみましょう!
モデルの情報を管理者ページに反映させる
前回作った管理者ページに、モデル情報を反映させてみます。
まだ、「ログイン機能」などはないので、先ほど作ったadminユーザのみ対応させてみましょう!
controllers.pyを修正
まず、以下の内容を追加でインポートします。
1 2 | import db from models import User, Task |
次に、 admin() を修正していきます。
現段階では動作確認なので、adminユーザのみを取得し、それに紐づいたタスクをデータベースから取得します。
また、データを取得した後は、セッションをクローズするのを忘れないようにしましょう。
1 2 3 4 5 6 7 8 9 10 11 | def admin(request: Request): # ユーザとタスクを取得 # とりあえず今はadminユーザのみ取得 user = db.session.query(User).filter(User.username == 'admin').first() task = db.session.query(Task).filter(Task.user_id == user.id).all() db.session.close() return templates.TemplateResponse('admin.html', {'request': request, 'user': user, 'task': task}) |
ビュー templates/admin.htmlを修正
次に、ビューを修正します。
取得したタスクは、テーブルとして表示するようにしてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | {% extends "layout.html" %} {% block content %} <br> <h2>Administrator page</h2> <hr> <p>Hi, {{ user['username'] }}.</p> <br> <table class="table"> <thead class="table-dark"> <tr> <th>#</th> <th>内容</th> <th>締め切り</th> <th>掲載日</th> <th>終了</th> </tr> </thead> <tbody> {% for t in task %} <tr> <td>{{ t['id'] }}</td> <td>{{ t['content'] }}</td> <td>{{ t['deadline'] }}</td> <td>{{ t['date'] }}</td> <td>{% if t['done'] %} <div class="text-success">済</div> {% else %} <div class="text-danger">未</div> {% endif %} </td> </tr> {% endfor %} </tbody> </table> {% endblock %} |
確認
では、http://127.0.0.1:8000/admin にアクセスしてみましょう。

改良したadminページ
うまくモデルからデータを取得できていそうです!
第3回へつづく!
今回は、モデルの構築をメインに行いました。
ただ、まだまだ「ToDoアプリ」とは言えませんね。
「ログイン機能」はない上に、ビューからToDoリストに「タスク追加」もできません。
次回は、そのあたりを中心に解説・実装していきたいと思います!
第3回の記事はこちら
ResponderとFastAPIを実際に使って比較してみた
こちらの記事もオススメ!
書いた人はこんな人

- 「好きを仕事にするエンジニア集団」の(株)ライトコードです!
ライトコードは、福岡、東京、大阪の3拠点で事業展開するIT企業です。
現在は、国内を代表する大手IT企業を取引先にもち、ITシステムの受託事業が中心。
いずれも直取引で、月間PV数1億を超えるWebサービスのシステム開発・運営、インフラの構築・運用に携わっています。
システム開発依頼・お見積もり大歓迎!
また、現在「WEBエンジニア」「モバイルエンジニア」「営業」「WEBデザイナー」「WEBディレクター」を積極採用中です!
インターンや新卒採用も行っております。
以下よりご応募をお待ちしております!
https://rightcode.co.jp/recruit
ライトコードの日常12月 1, 2023ライトコードクエスト〜東京オフィス歴史編〜
ITエンタメ10月 13, 2023Netflixの成功はレコメンドエンジン?
ライトコードの日常8月 30, 2023退職者の最終出社日に密着してみた!
ITエンタメ8月 3, 2023世界初の量産型ポータブルコンピュータを開発したのに倒産!?アダム・オズボーン