FastAPIでメールフォームを作ってみた件
今日はFASTAPIを用いてメールフォームを作成していて、やっとこさ動いたので、アウトプットする。
参考記事
STEP①利用するモジュールをインポートしていく
今回のメールフォーム作成では「FastAPI-Mail」モジュールを用いて作成する。
インポートの仕方は下記を参照。
仮想環境にインストールしていない人は、インストールしてね。
ここで「FastAPI-Mail」、「MessageSchema」、「FastMail」、「ConnctionConfig」それぞれの機能と使い方について理解しておこう。
利用するモジュールの機能
- FastAPI-Mail :電子メールを簡単に行うことができるモジュール
- MessageSchema :送信されるメールの詳細情報を設定する
- ConnectionConfig:SMTPサーバーの接続設定をするためのクラス。
- FastMail:メース送信に関する機能を提供するクラス
利用するモジュールの使い方
- fastapi_mail
- インストール
- SMTP接続情報の設定
- FastMailオブジェクトの初期化
- ConnectionConfig
- MesseageSchema (MessageSchemaオブジェクトを生成し、オブジェクト内にメールの情報を記載する
メールフォーム作成の流れ
- fastapi_emailモジュールから、MesseageSchemaとFastMail、ConnectionConfig関数をインストール
- ConnectionConfigで、メールサーバーとの接続を書く
- ここで詰まった。どうやら参考記事と自分が使っていたfastapi_mailのバージョンが違っていて、最新は書き方が変わったらしい。
- FastMailのインスタンスを生成
-
fm = FastMail(conf)
- ConnectionConfigをFastMailの引数として渡して、SMTP設定を引き継がせる。これにより、FastMailオブジェクトを使い、メールが送信できるようになる。FastMailオブジェクトがないと、メールの送信ができない。
-
- pydanticのEmailSchemaクラスを用いて、メール送信時の各フィールドのバリデーションを行う
-
class EmailSchema(BaseModel):name:stremail:strcontents: strtel: str
- これがないと、POSTで受け取った時に型が不一致になってエラーの原因になるので、優秀なクラスっぽい。pydanticすげえ。。。
-
- 情報がpostされた時に行う処理をルーティング含め書いていく
- MessageSchemaで、受信時のメッセージを作成していく
- returnに、ユーザー側に表示される内容を作っていく。これthanks.htmlとかでもええかも
コードを書き終えて、フォームからPOSTすると、下記のエラーになった。
{"detail":[{"loc":["body"],"msg":"value is not a valid dict","type":"type_error.dict"}]}
エラーが意味するのは、フォームからPOSTされた値のバリデーションが辞書型ではなく、正しくないですよ。とのこと。
FastAPIはデフォルトだと、JSON形式のデータを受け取ることを想定している。JSON形式のデータというのは、「KeyとValueのペアである辞書型」なのだ。
今回の場合、フォームから送信されたデータをPydanticモデル(EmailSchema)に従って
バリデーションし、データの形式を確認する。
しかし、HTMLフォームからデータを送信する場合、
multipart/form-dataエンコーディングが使用されるらしい。そしてmultipart/form-dataエンコーディングは、フォームのフィールドを個別のパートとして送信するそうな。
そのため、FASTAPIがEmailSchemaを通して、自動でバリデーションできなくなるよう。
ここでコードを下記のように変更した。
旧コード:
async def submit_form(email: EmailSchema):
email:EmailSchemaというのは、関数submit_formに引数emailを渡している。そして型はEmailSchemaだよということ。受け取ったデータを、EmailSchemaを通して、バリデーションするのだ。
新コード:
Herokuでデプロイしたのにエラーになる場合に確認しておくべきこと
ターミナルからherokuへデプロイをしたのに、エラーになってしまった。
デプロイ方法と共にエラー解消方法を記載する
Herokuへデプロイする手順
- gunicornをインストール
- pip install gunicorn
- requirements.txtを作成
- pip freeze > requirements.txt
- Procfileを作成
- 中身に「web: gunicorn app:app --log-file=-」と記載
- デプロイするリポジトリを作成
- git init
- git add .
- git commit -m "first-commit"
- heroku上にデプロイを進める
- heroku login
- heroku create APPNAME
- git push heroku main
- git brunchで、ブランチ名を確認しておこう。masterの場合もある
- デプロイ完了
以上がherokuへデプロイする手順。が、ここでエラー発生。デプロイをしたが
アプリケーションが表示されない。
herokuへデプロイ時に発生したエラー名
There's nothing here, yet.
ここにはまだ何もないよって言われた。では下記からはエラーの解消方法を記載していく。どうやらherokuが無料版から有料になったことで、アプリケーションの承認が必要になったらしい。
デプロイ時のエラー解消手順
- herokuのサイトにログイン
- デプロイしたアプリケーションを選択
- Resourceタブを選択し、$0.00の右にある鉛筆マークをクリック
- トグルを選択したら、confirmをクリック
- 下記の画像になれば、設定完了。再度、デプロイ先のURLにアクセス
以上で、デプロイ後に表示が可能となる!!
それでは、Good Python Life!
めんどい人は、下記のURLに直接アクセスして、アプリケーションの課金承認を行うのもおすすめ。
Flaskのアプリケーションコンテキストというやつを理解したい
今回は、PythonのFlaskを用いてTodoリストを作る練習をしていたのですが、
DB作成のタイミングdb.create_all()でコケてしまったので、コケた理由と対処方法についてアウトプットしていく。
教材としていたのは、下記Youtube。
めちゃくちゃわかりやすいので、Progate終わってなにしようかな?って
迷っている人におすすめです。
動画の中で、DBを構築する部分があるのですが、動画通りに進めると
エラーになってしまいました。
コンソールに表示されていたエラーは、下記になります。
raise RuntimeError(unbound_message) from None
RuntimeError: Working outside of application context.This typically means that you attempted to use functionality that needed
the current application. To solve this, set up an application context
with app.app_context(). See the documentation for more information.
エラーメッセージの意味:Flaskアプリケーションコンテキスト外で機能を使用しようとした
はい。意味がわからなすぎます。アプリケーションコンテキストってなに???
聞いたことありません。ということで、下記からは、「アプリケーションコンテキストの意味」・「本エラーの解決方法」を説明します。
アプリケーションコンテキストってなんぞや?意味を理解しよう
アプリケーションコンテキストとは、Flaskアプリケーションの中で実行される処理に対して必要な情報を提供するための仕組み
とのこと。要は、アプリケーションコンテキストは、インスタンス・データベースセッションなど、Flaskアプリケーションに関する情報が全部ぶち込まれてるデカいオブジェクトだと。
アプリケーションコンテキストを作成しないと、どこにアクセスすればいいかわからなくなっちゃうんですね。可愛い。
そして、アプリケーションコンテキストは、HTTPリクエストを行う際に、自動で作成されるようです。今回の教材では、HTTPリクエストを行わずに、コンソールから直接アクセスする形式だったので、アプリケーションコンテキストを作成する必要があったのかな・・・
該当のエラーを解決する
さて、今回のエラーを解決するステップを記載していく
- コンソールでpythonを実行し、対話モードにする
- Flaskアプリケーションをインポート
- from app import app
- アプリケーションコンテキストを生成
- app.app_context().push()
- pyファイルから、DBモジュールをインポート
- from app import db
- db.create_all()でDBを生成
- db.create_all()
上記の手順で進めていけば、DBが生成できるよ。
面白いなと思ったのは、ステップ3のアプリケーションコンテキスト生成のタイミングでinstanceディレクトリができたこと。アプリケーションコンテキストを生成すると、Flaskアプリケーションのインスタンスが生成されて、情報にアクセスできるようになるんかね。