【第2回】FastAPIチュートリアル: ToDoアプリを作ってみよう【モデル構築編】
IT技術
FastAPIチュートリアル: toDoアプリを作ってみよう~第2回~
前回の記事「【第1回】FastAPIチュートリアル: ToDoアプリを作ってみよう【環境構築編】」の続きです。
今回も、ToDoアプリを作る過程で、FastAPIの使い方を学んでいきましょう!
第1回はこちら
情報の管理
今回の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 を用いて記述していきます。
コードを書く前に、作りたいモデルを整理しましょう。
モデル例
例えば、以下のようなモデルが考えられます。
User | id (各ユーザ固有のID: 主キー) username (ログインに必要なユーザネーム) password (ログインに必要なパスワード) mail (メールアドレス) |
Task | id (各タスク固有の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 にアクセスしてみましょう。
うまくモデルからデータを取得できていそうです!
第3回へつづく!
今回は、モデルの構築をメインに行いました。
ただ、まだまだ「ToDoアプリ」とは言えませんね。
「ログイン機能」はない上に、ビューからToDoリストに「タスク追加」もできません。
次回は、そのあたりを中心に解説・実装していきたいと思います!
第3回の記事はこちら
ResponderとFastAPIを実際に使って比較してみた
こちらの記事もオススメ!
2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...
2020.07.30Python 特集実装編※最新記事順Responder + Firestore でモダンかつサーバーレスなブログシステムを作ってみた!P...
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ
「好きを仕事にするエンジニア集団」の(株)ライトコードです! ライトコードは、福岡、東京、大阪、名古屋の4拠点で事業展開するIT企業です。 現在は、国内を代表する大手IT企業を取引先にもち、ITシステムの受託事業が中心。 いずれも直取引で、月間PV数1億を超えるWebサービスのシステム開発・運営、インフラの構築・運用に携わっています。 システム開発依頼・お見積もり大歓迎! また、現在「WEBエンジニア」「モバイルエンジニア」「営業」「WEBデザイナー」を積極採用中です! インターンや新卒採用も行っております。 以下よりご応募をお待ちしております! https://rightcode.co.jp/recruit
おすすめ記事
immichを知ってほしい
2024.10.31