FastAPIでメールフォームを作ってみた件

今日はFASTAPIを用いてメールフォームを作成していて、やっとこさ動いたので、アウトプットする。

 

参考記事

STEP①利用するモジュールをインポートしていく

今回のメールフォーム作成では「FastAPI-Mail」モジュールを用いて作成する。

インポートの仕方は下記を参照。

 from fastapi_mail import MessageSchema, FastMail, ConnectionConfig

仮想環境にインストールしていない人は、インストールしてね。

 pip install fastapi-mail

 

ここで「FastAPI-Mail」、「MessageSchema」、「FastMail」、「ConnctionConfig」それぞれの機能と使い方について理解しておこう。

 

利用するモジュールの機能

  • FastAPI-Mail :電子メールを簡単に行うことができるモジュール 
    • 電子メールの送信機能
      • SMTPサーバーを設定し、送信が可能。併せて宛先・件名・本文・添付ファイルなど、メールの内容の指定ができる
    • メールテンプレート機能
      • メールの本文をHTML形式で作成する機能
    • メールの非同期送信
      • 非同期タスクとしてメールの送信が可能
    • エラーに対する処理機能
      • SMTPサーバーへの接続エラー、メールの送信エラーの検出。またエラー内容の取得が可能。
      • エラー時に代替アクションの提供も可能。
  • MessageSchema :送信されるメールの詳細情報を設定する
  • ConnectionConfig:SMTPサーバーの接続設定をするためのクラス。
  • FastMail:メース送信に関する機能を提供するクラス

利用するモジュールの使い方

  • fastapi_mail
    1. インストール
    2. SMTP接続情報の設定
    3. FastMailオブジェクトの初期化
  • ConnectionConfig
    • MAIL_USERNAME:SMTPサーバー接続時のユーザー名
    • MAIL_PASSWORD:SMTPサーバー接続時のパスワード
    • MAIL_FROM:送信元アドレスの設定
    • MAIL_PORT:SMTPサーバーのポート番号を指定
    • MAIL_SERVER:SMTPのサーバー名を入力
    • MAIL_TLSTLSを使用するか
    • MAIL_SSLSSLを使用するか
  • MesseageSchema (MessageSchemaオブジェクトを生成し、オブジェクト内にメールの情報を記載する
    • subject:メール件名
    • recipients:メールの送り先アドレスを指定
    • body:メールの本文を指定
    • plain_text:メールの本文をプレーンテキスト形式で指定する場合に使用。bodyとは別に指定可能。
    • html:メールの本文をHTML形式で指定する場合に使用。bodyとは別に指定可能
    • cc:ccアドレスの指定
    • bccbccアドレスの指定
    • attachments:メールに添付するファイルのリストを指定

 

 メールフォーム作成の流れ

  1. fastapi_emailモジュールから、MesseageSchemaとFastMail、ConnectionConfig関数をインストール
  2. ConnectionConfigで、メールサーバーとの接続を書く
    1. ここで詰まった。どうやら参考記事と自分が使っていたfastapi_mailのバージョンが違っていて、最新は書き方が変わったらしい。
    2.  MAIL_STARTTLS=True
        MAIL_SSL_TLS=False
  3. FastMailのインスタンスを生成 
    1. fm = FastMail(conf)
    2. ConnectionConfigをFastMailの引数として渡して、SMTP設定を引き継がせる。これにより、FastMailオブジェクトを使い、メールが送信できるようになる。FastMailオブジェクトがないと、メールの送信ができない。
  4. pydanticのEmailSchemaクラスを用いて、メール送信時の各フィールドのバリデーションを行う
    1.  class EmailSchema(BaseModel):
       name:str
       email:str
         contents: str
       tel: str
    2. これがないと、POSTで受け取った時に型が不一致になってエラーの原因になるので、優秀なクラスっぽい。pydanticすげえ。。。
  5. 情報がpostされた時に行う処理をルーティング含め書いていく
    1. MessageSchemaで、受信時のメッセージを作成していく
    2. 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を通して、バリデーションするのだ。

 

新コード:

async def submit_form(name: str = Form(...), tel: str = Form(...),
email: str = Form(...), contents: str = Form(...)):
 

 

 

 

 

Herokuでデプロイしたのにエラーになる場合に確認しておくべきこと

ターミナルからherokuへデプロイをしたのに、エラーになってしまった。

デプロイ方法と共にエラー解消方法を記載する

 

Herokuへデプロイする手順

  1. gunicornをインストール
    1. pip install gunicorn
  2. requirements.txtを作成
    1. pip freeze > requirements.txt
  3. Procfileを作成
    1. 中身に「web: gunicorn app:app --log-file=-」と記載
  4. デプロイするリポジトリを作成
    1. git init
    2. git add . 
    3. git commit -m "first-commit"
  5. heroku上にデプロイを進める
    1. heroku login
    2. heroku create APPNAME
    3. git push heroku main
      1. git brunchで、ブランチ名を確認しておこう。masterの場合もある
    4. デプロイ完了

 

以上がherokuへデプロイする手順。が、ここでエラー発生。デプロイをしたが

アプリケーションが表示されない。

 

herokuへデプロイ時に発生したエラー名

There's nothing here, yet.

ここにはまだ何もないよって言われた。では下記からはエラーの解消方法を記載していく。どうやらherokuが無料版から有料になったことで、アプリケーションの承認が必要になったらしい。

 

デプロイ時のエラー解消手順

  1. herokuのサイトにログイン 

    https://dashboard.heroku.com/apps

  2. デプロイしたアプリケーションを選択
  3. Resourceタブを選択し、$0.00の右にある鉛筆マークをクリック

  4. トグルを選択したら、confirmをクリック

  5. 下記の画像になれば、設定完了。再度、デプロイ先のURLにアクセス

 

 

以上で、デプロイ後に表示が可能となる!!

それでは、Good Python Life!

 

めんどい人は、下記のURLに直接アクセスして、アプリケーションの課金承認を行うのもおすすめ。

https://heroku.com/verify

 

 

Flaskのアプリケーションコンテキストというやつを理解したい

今回は、PythonのFlaskを用いてTodoリストを作る練習をしていたのですが、

DB作成のタイミングdb.create_all()でコケてしまったので、コケた理由と対処方法についてアウトプットしていく。

 

教材としていたのは、下記Youtube

 

youtu.be

 

めちゃくちゃわかりやすいので、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リクエストを行わずに、コンソールから直接アクセスする形式だったので、アプリケーションコンテキストを作成する必要があったのかな・・・

 

該当のエラーを解決する

さて、今回のエラーを解決するステップを記載していく

  1. コンソールでpythonを実行し、対話モードにする
  2.  Flaskアプリケーションをインポート
    1. from app import app
  3. アプリケーションコンテキストを生成
    1. app.app_context().push()
  4. pyファイルから、DBモジュールをインポート
    1. from app import db
  5. db.create_all()でDBを生成
    1. db.create_all()

 

上記の手順で進めていけば、DBが生成できるよ。

 

面白いなと思ったのは、ステップ3のアプリケーションコンテキスト生成のタイミングでinstanceディレクトリができたこと。アプリケーションコンテキストを生成すると、Flaskアプリケーションのインスタンスが生成されて、情報にアクセスできるようになるんかね。