このガイドの目的は、かなり機能的なマイクロブログアプリケーションを開発することです。オリジナリティが完全に欠如していることから、
microblog
と呼ぶことにしました。
目次
短い繰り返し
前回のレッスンでは、主にデータベースに関連する改善に焦点を当てました。
今日は、データベースを少しリラックスさせ、代わりに、ほとんどのWebアプリケーションが持っている1つの非常に重要な機能を見てみましょう。それは、ユーザーにメールを送信する機能です。
小規模なアプリケーションでは、同様の機能を実装します。誰かがサブスクライブするたびにユーザーに通知を送信します。 submit関数が役立つ可能性のあるものがさらにいくつかあります。そのため、再利用できるように関数を設計しようとします。
Flask-Mailの概要
幸いなことに、Flaskには既に電子メールを処理する拡張機能があり、タスクの100%を実行しませんが、これに非常に近いです。
仮想環境へのFlask-Mailのインストールは非常に簡単です。 Windows以外のシステムのユーザーは以下を行う必要があります。
flask/bin/pip install flask-mail
Windowsユーザーの場合、Flask-Mailの依存関係の1つがこのOSでは機能しないため、事態はもう少し複雑です。 Windowsでは、次のことを行う必要があります。
flask\Scripts\pip install --no-deps lamson chardet flask-mail
構成
以前、ユニットテストを追加するときに、Flaskの構成を追加しました。この構成では、アプリケーションの製品版でエラーに関する通知を送信する必要がある電子メールを指定しました。 同じ情報は、アプリケーションによってメールを送信するために使用されます。
次の情報が必要であることを覚えておく必要があります。
- 電子メールが送信されるサーバー
- 管理者のメール
これは、前の記事(
config.py
)で行ったことです。
# email server MAIL_SERVER = 'your.mailserver.com' MAIL_PORT = 25 MAIL_USE_TLS = False MAIL_USE_SSL = False MAIL_USERNAME = 'you' MAIL_PASSWORD = 'your-password' # administrator list ADMINS = ['you@example.com']
もちろん、アプリケーションが実際にメールを送信できるように、この設定に実際のデータを入力する必要があります。 たとえば、アプリケーションを使用してgmail.com経由で手紙を送信する場合は、次を指定する必要があります。
# email server MAIL_SERVER = 'smtp.googlemail.com' MAIL_PORT = 465 MAIL_USE_TLS = False MAIL_USE_SSL = True MAIL_USERNAME = 'your-gmail-username' MAIL_PASSWORD = 'your-gmail-password' # administrator list ADMINS = ['your-gmail-username@gmail.com']
Mailオブジェクトも初期化する必要があります。 これは、SMTPサーバーに接続し、メールを送信するオブジェクトになります(
app/__init__.py
):
from flask.ext.mail import Mail mail = Mail(app)
メールを送信しましょう。
Flask-Mailの仕組みを理解するには、コマンドラインから電子メールを送信する必要があります。 仮想環境からPythonを実行して、次のように入力します。
>>> from flask.ext.mail import Message >>> from app import app, mail >>> from config import ADMINS >>> msg = Message('test subject', sender = ADMINS[0], recipients = ADMINS) >>> msg.body = 'text body' >>> msg.html = '<b>HTML</b> body' >>> with app.app_context(): ... mail.send(msg) ....
上記のコードスニペットは、
config.py
指定された管理者のリストにメールを送信します。 送信者はリストから最初の管理者を示します。 レターにはテキストバージョンとHTMLバージョンが含まれます。表示される内容は、メールクライアントの設定によって異なります。 メールを送信するには
app_context
を作成する必要があることに注意してください。 最近のFlask-Mailリリースではこれが必要です。 Flaskがリクエストを処理すると、コンテキストが自動的に作成されます。
クエリの内部にいないため、手作業でコンテキストを作成できます。
次は、このコードをアプリケーションに統合します。
シンプルなメールフレームワーク
次に、電子メールを送信する補助関数を作成します。 これは、上記のテストのより一般的なバージョンです。 この関数を新しいファイルに追加し、電子メール関連の関数に割り当てます(ファイル
app/emails.py
):
from flask.ext.mail import Message from app import mail def send_email(subject, sender, recipients, text_body, html_body): msg = Message(subject, sender = sender, recipients = recipients) msg.body = text_body msg.html = html_body mail.send(msg)
Flask-mailは使用する以上のものをサポートしていることに注意してください。 たとえば、ブラインドコピーと添付ファイルのリストは利用可能ですが、アプリケーションでは使用しません。
サブスクリプション通知
電子メールを送信するための基本的なフレームワークができたので、サブスクライバーに通知する関数(ファイル
app/emails.py
)を
app/emails.py
できます。
from flask import render_template from config import ADMINS def follower_notification(followed, follower): send_email("[microblog] %s is now following you!" % follower.nickname, ADMINS[0], [followed.email], render_template("follower_email.txt", user = followed, follower = follower), render_template("follower_email.html", user = followed, follower = follower))
予期しない何かを見ましたか?
私たちの旧友
render_template
関数は、レターの外観を作成します。 覚えているなら、この関数を使用して、ビューからすべてのHTMLテンプレートをレンダリングしました。 HTMLと同様に、レターの本文はテンプレートを使用するための理想的な候補です。 可能な限り、ロジックをビューから分離し、文字が他のビューと一緒にテンプレートフォルダーに入るようにします。
そこで、通知用のテキストおよびHTMLバージョンのテンプレートを作成します。 これはテキストバージョンです(ファイル
app/templates/follower_email.txt
):
Dear {{user.nickname}}, {{follower.nickname}} is now a follower. Click on the following link to visit {{follower.nickname}}'s profile page: {{url_for('user', nickname = follower.nickname, _external = True)}} Regards, The microblog admin
HTMLバージョンでは、すべてを少しきれいにし、サブスクライバーのアバターとプロフィール情報(
app/templates/follower_email.html
ファイル)を表示できます:
<p>Dear {{user.nickname}},</p> <p><a href="{{url_for('user', nickname = follower.nickname, _external = True)}}">{{follower.nickname}}</a> is now a follower.</p> <table> <tr valign="top"> <td><img src=""></td> <td> <a href="{{url_for('user', nickname = follower.nickname, _external = True)}}">{{follower.nickname}}</a><br /> {{follower.about_me}} </td> </tr> </table> <p>Regards,</p> <p>The <code>microblog</code> admin</p>
テンプレートの
url_for
フィールドの
_external = True
に注意してください。 デフォルトでは、
url_for
関数は現在のページに関連するURLを生成します。 たとえば、コード{url_for( "index")}は
/index
になり
/index
、
http://localhost:5000/index
期待され
http://localhost:5000/index
。 電子メールにはドメインコンテキストがないため、ドメインを含む完全なURLを提供する必要があります
_external
は
_external
を支援し
_external
。
最後のステップは、
"Follow"
(ファイル
app/views.py
)を処理する送信機能を備えた電子メールの送信を有効にすることです。
from emails import follower_notification @app.route('/follow/<nickname>') @login_required def follow(nickname): user = User.query.filter_by(nickname = nickname).first() # ... follower_notification(user, g.user) return redirect(url_for('user', nickname = nickname))
ここで、2人のユーザーを作成し(まだ作成していない場合)、メール通知がどのように機能するかを確認するために、一方のサブスクライバーを他方のサブスクライバーにする必要があります。 それはあなたが必要なものですか? 終わった?
これで、よくできた仕事を探して、実装する必要がある機能のリストから電子メール通知を消すことができます。
しかし、アプリケーションで遊んだ場合、「フォロー」をクリックした後、ブラウザがページを更新するまでに数秒かかる電子メール通知が実装されたことがわかりました。 そして、これが即座に起こる前に。
それで何が起こったのですか?
問題は、Flask-Mailが電子メールを同期的に送信することです。 レターの送信中はサーバーがブロックされ、メッセージが配信されたときにのみブラウザーに応答が送信されます。 低速のサーバーにメールを送信しようとした場合、さらに悪いことにシャットダウンした場合に何が起こるか想像できますか? これは悪いです。
これは非常に恐ろしい制限です。メールの送信は、サーバーに干渉しないバックグラウンドタスクである必要があるため、すべてを修正する方法を見てみましょう。
Python非同期呼び出し
電子メールの送信作業がバックグラウンドで実行されている間に、
send_email
関数
send_email
即座
send_email
完了するようにします。
Pythonは、複数の方法でも非同期タスクの実行を既にサポートしていることがわかりました。 スレッド化およびマルチプロセッシングモジュールが役立ちます。
電子メールを送信する必要があるたびに新しいスレッドを開始することは、新しいプロセスを開始するよりもリソースをあまり消費しない操作なので、
mail.send(msg)
呼び出しをストリーム(
app/emails.py
)に移動しましょう。
from threading import Thread def send_async_email(msg): mail.send(msg) def send_email(subject, sender, recipients, text_body, html_body): msg = Message(subject, sender = sender, recipients = recipients) msg.body = text_body msg.html = html_body thr = Thread(target = send_async_email, args = [msg]) thr.start()
「フォロー」機能をテストしている場合、レターが送信される前にブラウザに更新されたページが表示されることがわかります。
そこで、非同期メール送信を実装しましたが、将来他の非同期機能を実装する必要がある場合はどうでしょうか? 手順は同じままですが、各ケースでストリームを操作するためのコードを複製する必要がありますが、これは良くありません。
ソリューションをデコレータとして実装できます。 デコレータを使用すると、上記のコードは次のように変更されます。
from decorators import async @async def send_async_email(msg): mail.send(msg) def send_email(subject, sender, recipients, text_body, html_body): msg = Message(subject, sender = sender, recipients = recipients) msg.body = text_body msg.html = html_body send_async_email(msg)
はるかに良いですね。
この魔法を行うコードは実際には非常に簡単です。 新しいファイル(ファイル
app/decorators.py
)に書き込みます:
from threading import Thread def async(f): def wrapper(*args, **kwargs): thr = Thread(target = f, args = args, kwargs = kwargs) thr.start() return wrapper
非同期タスクの適切な基盤を誤って作成したため、すべてが完了したと言えます!
演習のために、スレッドの代わりにプロセスを使用した場合に決定がどのように変わるかを見てみましょう。
メールを送信するたびに新しいプロセスを開始するのではなく、
multiprocessing
モジュールの
Pool
クラスを使用できます。 このクラスは、必要な数のプロセス(メインプロセスの分岐)を作成し、すべてのプロセスは
apply_async
メソッドを介して渡されるタスクを待機します。 これは忙しいサイトにとっては便利で興味深いかもしれませんが、今のところはストリームに焦点を当てます。
最後の言葉
更新されたアプリケーションのソースコードは以下から入手できます。
microblog-0.11.zipをダウンロードします。
このアプリケーションをGitHubまたは同様のサイトに投稿するようにいくつかのリクエストを受け取りました。これは非常に良い考えだと思います。 私は近い将来これに取り組んでいきます。 連絡を取り合いましょう。
一連のチュートリアルをご覧いただきありがとうございます。 次のパートでお会いしましょう。
ミゲル