Flask Web開発学習稿(三)
第六章電子メール
特定のイベントが発生したときにユーザーに注意する必要がある場合、
コンフィギュレーション
デフォルト
説明
MAIL_SERVER
localhost
Emailサーバのipアドレスまたはホスト名
MAIL_PORT
25
Emailサーバポート
MAIL_USE_TLS
False
トランスポート・レイヤ・セキュリティ・プロトコルの有効化
MAIL_USE_SSL
False
コンドーム接続プロトコルの有効化
MAIL_USERNAME
None
Emailユーザー名
MAIL_PASSWORD
None
Emailパスワード
外部SMTPサーバの使用がより便利
アカウントとパスワードをプログラムに書くのは安全ではありません.機密情報を保護するために、スクリプトを環境変数からインポートする必要があります.
環境変数の設定方法
プログラム内のEメール送信機能の統合
この2つのプログラム固有の構成項目は、メールトピックの接頭辞と送信者のアドレスをそれぞれ定義する
純粋なテキストとHTML版のメール本文をレンダリングするための2つのテンプレートファイルを作成します.この2つのテンプレートファイルは、通常のテンプレートと区別するためにtmplatesフォルダの下のmailサブフォルダに保存されます.電子メールのテンプレートにユーザーがテンプレートパラメータを持つ必要があるためsend_を呼び出すmail()関数をキーワードパラメータとしてユーザーに渡すというプログラムは、メールを送信する際に一時的にブロックされ、電子メールを非同期で送信して不要な遅延を解消します.
多くのFlask拡張では、アクティブ化されたプログラムコンテキストと要求コンテキストがすでに存在すると仮定しています.Flask-Mailの
第七章大型プログラムの構造
多くの機能の学習を完了しましたが、プログラムが大きくなるにつれて、大規模なプログラムの構造をどのように組織するかを学びます.
7.1プロジェクト構造
7.2構成オプション
これからは、以前のように簡単な辞書構造ではなく、構成クラスを使用します.
構成クラスは、プログラムインスタンスである
7.3パッケージ
パッケージは、プログラムのすべてのコード、テンプレート、および静的ファイルを保存するために使用されます.
7.3.1プログラムファクトリ関数の使用
作成プロセスを明示的に呼び出すことができるファクトリ関数に移動すると、プログラムのファクトリ関数はappパッケージのコンストラクションファイルで定義されたコンストラクションファイルに使用中のFlask拡張子が多すぎて、まだ初期化に必要なプログラムインスタンスがないため、初期化拡張がなく、拡張クラスを作成する際にコンストラクション関数にパラメータ
7.3.2ブルーブックでプログラムの機能を実現する
単一スクリプトプログラムでは、プログラムインスタンスはグローバルな役割ドメインに存在し、ルーティングはappを直接使用することができる.route修飾器定義.しかし、現在、アプリケーション・インスタンスは実行時に作成され、
ブルーブッククラスオブジェクトをインスタンス化することでブルーブックを作成できます.コンストラクション関数には、ブルーブックの名前とブルーブックが存在するモジュールまたはパッケージの2つのパラメータがあります.多くの場合、Pythonの
エラーハンドラは以下の通りです.
ブルーブックでのビュー関数の違いルーティング修飾器は、ブルーブックによって を提供する. Flaskは、ブルーブックのすべてのエンドポイントにネーミングスペースを追加します.これにより、競合することなく、異なるブルーブックで同じエンドポイント名を使用してビュー関数を定義できます.ネーミングスペースは、ブルーブックの名前(ブルーブックコンストラクタの最初のパラメータ)であるため、ビュー関数
7.4起動スクリプト
スクリプトは、まずアプリケーション・インスタンスを作成し、システム環境から
7.5需要書類
pipは、次のコマンドを使用して、このファイル
7.6ユニットテスト
テストは典型的なユニットテストの書き方に従って構築され、実行中のプログラムと同様に、まずテスト構成を使用してプログラムを作成し、コンテキストをアクティブにします.
最初のテストでは、アプリケーション・インスタンスが存在することを確認し、2番目のテスト・アプリケーション・インスタンスがテスト構成で実行されます.テストフォルダに正しいパッケージ構造があることを確認するために、
ユニットテストを実行するためにmanage.pyにカスタムコマンドを追加し、
運転方法は以下の通りです.
7.7データベースの作成
環境変数からデータベースのURLを読み込むことを優先します.また、次のコマンドを使用してデータテーブルを作成したり、最新のリビジョン
特定のイベントが発生したときにユーザーに注意する必要がある場合、
smtplib
をパッケージしたFlask-Mail
拡張機能は、Flaskとpip install flask-mail
Flask-Mailを統合してSMTPサーバに接続することができ、構成しない場合、Flask-Mailはlocalhostのポート25に接続されます.コンフィギュレーション
デフォルト
説明
MAIL_SERVER
localhost
Emailサーバのipアドレスまたはホスト名
MAIL_PORT
25
Emailサーバポート
MAIL_USE_TLS
False
トランスポート・レイヤ・セキュリティ・プロトコルの有効化
MAIL_USE_SSL
False
コンドーム接続プロトコルの有効化
MAIL_USERNAME
None
Emailユーザー名
MAIL_PASSWORD
None
Emailパスワード
外部SMTPサーバの使用がより便利
from flask.ext.mail import Mail
app.config['MAIL_SERVER'] = 'mail.xxx.com'
app.config['MAIL_PORT'] = '587'
app.config['MAIL_USE_TLS'] = 'True'
app.config['MAIL_USERNAME'] = 'username'
app.config['MAIL_PASSWORD'] = 'pwd'
mail = Mail(app)
アカウントとパスワードをプログラムに書くのは安全ではありません.機密情報を保護するために、スクリプトを環境変数からインポートする必要があります.
app.config['MAIL_USERNAME'] = os.environ.get('MALI_USERNAME')
app.config['MAIL_PASSWORD'] = os.environ.get('MALI_PASSWORD')
環境変数の設定方法
# Linux Mac OS X
export MALI_USERNAME=<YOU_USERNAME>
export MALI_PASSWORD=<YOU_PASSWORD>
# Windows
set MALI_USERNAME=<YOU_USERNAME>
set MALI_PASSWORD=<YOU_PASSWORD>
プログラム内のEメール送信機能の統合
from flask.ext.mail import Message
app.config['FLASKY_MAIL_SUBJECT_PREFIX'] = '[Flasky]'
app.config['FLASKY_MAIL_SENDER'] = 'Flasky Admin <[email protected]>'
def send_email(to, subject, template, **kwargs):
msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + subject,
sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to])
msg.body = render_template(template + '.txt', **kwargs)
msg.html = render_template(template + '.html', **kwargs)
mail.send(msg)
この2つのプログラム固有の構成項目は、メールトピックの接頭辞と送信者のアドレスをそれぞれ定義する
send_email()
関数のパラメータが、それぞれ受信者アドレス、トピック、メール本文のテンプレートとキーワードパラメータリストでテンプレートを指定する際に拡張子を含めることはできません.これにより、2つのテンプレートを使用して、純粋なテキスト本文とリッチテキスト本文の呼び出し元をそれぞれレンダリングし、テンプレートで使用するためにキーワードパラメータをrender_template()
関数に渡し、電子メール本文を生成します.次に、ビュー関数を変更します.app.config['FLASKY_ADMIN'] = os.environ.get('FLASKY_ADMIN')
#...
@app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.name.data).first()
if user is None:
user = User(username=form.name.data)
db.session.add(user)
session['known'] = False
if app.config['FLASKY_ADMIN']:
send_email(app.config['FLASKY_ADMIN'], 'New User',
'mail/new_user', user=user)
else:
session['known'] = True
session['name'] = form.name.data
form.name.data = ''
return redirect(url_for('index'))
return render_template('index.html', form=form, name=session.get('name'), known=session.get('known', False))
純粋なテキストとHTML版のメール本文をレンダリングするための2つのテンプレートファイルを作成します.この2つのテンプレートファイルは、通常のテンプレートと区別するためにtmplatesフォルダの下のmailサブフォルダに保存されます.電子メールのテンプレートにユーザーがテンプレートパラメータを持つ必要があるためsend_を呼び出すmail()関数をキーワードパラメータとしてユーザーに渡すというプログラムは、メールを送信する際に一時的にブロックされ、電子メールを非同期で送信して不要な遅延を解消します.
from threading import Thread
def send_async_email(app, msg):
with app.app_context():
mail.send(msg)
def send_email(to, subject, template, **kwargs):
msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + subject,
sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to])
msg.body = render_template(template + '.txt', **kwargs)
msg.html = render_template(template + '.html', **kwargs)
thr = Thread(target=send_async_email, args=[app, msg])
thr.start()
return thr
多くのFlask拡張では、アクティブ化されたプログラムコンテキストと要求コンテキストがすでに存在すると仮定しています.Flask-Mailの
send()
関数はcurrent_app
を使用します.そのため、プログラムコンテキストをアクティブ化する必要がありますが、異なるスレッドでmail.send()
関数を実行する場合、プログラムコンテキストはapp.app_context()
を使用して手動で作成します.Eメールを大量に送信する必要がある場合、Celeryタスクキューの使用が適切第七章大型プログラムの構造
多くの機能の学習を完了しましたが、プログラムが大きくなるにつれて、大規模なプログラムの構造をどのように組織するかを学びます.
7.1プロジェクト構造
├─Flsky
│ │─app # Flask
│ | ├─static
│ | |─templates
│ | |─main
│ | | │-__init__.py
│ │ | |-errors.py
│ │ | |-forms.py
│ │ | |-views.py
│ │ |-__init__.py
│ │ |-email.py
│ │ |-models.py
| |-migrations #
| |-tests #
| | |-__init__.py
| | |-test*.py
│ |-config.py #
│ |-manage.py #
│ |-requirements.txt #
| └─ venv # python
7.2構成オプション
これからは、以前のように簡単な辞書構造ではなく、構成クラスを使用します.
#config.py
import os
basedir = os.path.abspath(os.path.dirname(__file__))
#
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string'
SQLALCHEMY_COMMIT_ON_TEARDOWN = True
FLASKY_MAIL_SUBJECT_PREFIX = '[Flasky]'
FLASKY_MAIL_SENDER = 'Flasky Admin <[email protected]>'
FLASKY_ADMIN = os.environ.get('FLASKY_ADMIN')
@staticmethod
def init_app(app):
pass
class DevelopmentConfig(Config): DEBUG = True
MAIL_SERVER = 'smtp.googlemail.com'
MAIL_PORT = 587
MAIL_USE_TLS = True
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')
class TestingConfig(Config):
TESTING = True
SQLALCHEMY_DATABASE_URI = os.environ.get('TEST_DATABASE_URL') or \ 'sqlite:///' + os.path.join(basedir, 'data-test.sqlite')
class ProductionConfig(Config):
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'data.sqlite')
config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig,
'default': DevelopmentConfig
}
構成クラスは、プログラムインスタンスである
init_app()
メソッドを定義することができ、このメソッドでは、現在の環境の構成の初期化を実行することができ、ベースクラスConfigのinit_app()
メソッドが空になります.7.3パッケージ
パッケージは、プログラムのすべてのコード、テンプレート、および静的ファイルを保存するために使用されます.
7.3.1プログラムファクトリ関数の使用
作成プロセスを明示的に呼び出すことができるファクトリ関数に移動すると、プログラムのファクトリ関数はappパッケージのコンストラクションファイルで定義されたコンストラクションファイルに使用中のFlask拡張子が多すぎて、まだ初期化に必要なプログラムインスタンスがないため、初期化拡張がなく、拡張クラスを作成する際にコンストラクション関数にパラメータ
create_app()
関数を入力しなかったのがプログラムのファクトリ関数である.プログラムが使用するコンフィギュレーション名コンフィギュレーションクラスがconfigであるパラメータを受け入れる.pyファイルで定義、保存する構成はFlask appを使用することができる.config構成オブジェクトが提供するfrom_object()
メソッドは、プログラムを直接インポートします.拡張オブジェクトについては、config辞書から名前で選択できます.プログラム作成構成が完了すると、拡張子を初期化できます.前に作成した拡張オブジェクトにinit_app()
を呼び出すと、初期化プロセスが完了します.# app/__init__.py
from flask import Flask, render_template
from flask.ext.bootstrap import Bootstrap
from flask.ext.mail import Mail
from flask.ext.moment import Moment
from flask.ext.sqlalchemy import SQLAlchemy
from config import config
bootstrap = Bootstrap()
mail = Mail()
moment = Moment()
db = SQLAlchemy()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
bootstrap.init_app(app)
mail.init_app(app)
moment.init_app(app)
db.init_app(app)
#
return app
7.3.2ブルーブックでプログラムの機能を実現する
単一スクリプトプログラムでは、プログラムインスタンスはグローバルな役割ドメインに存在し、ルーティングはappを直接使用することができる.route修飾器定義.しかし、現在、アプリケーション・インスタンスは実行時に作成され、
app.route
はcreate_app()
以降にのみ存在します.この場合、定義ルーティングが遅すぎて、ブルーブックに登録されているルーティングはスリープ状態にあり、ブルーブックがプログラムに登録されるまで、ルーティングは本当にプログラムの一部になります.グローバルな役割ドメインにあるブルーブックを使用する場合、ルーティングを定義する方法は、単一のスクリプトプログラムとほぼ同じです.柔軟性を最大化するために、パッケージにはブルーブックを保存するためのサブパッケージが作成されています.app/main/__init__.py
from flask import Blueprint
main = Blueprint('main', __name__)
from . import views, errors
ブルーブッククラスオブジェクトをインスタンス化することでブルーブックを作成できます.コンストラクション関数には、ブルーブックの名前とブルーブックが存在するモジュールまたはパッケージの2つのパラメータがあります.多くの場合、Pythonの
__name__
変数は、2番目のパラメータに必要な値アプリケーションのルーティングがapp/main/views.py
モジュールに、エラー処理がapp/main/errors.py
モジュールに配置されます.これらのモジュールをインポートすると,ルーティングとエラー処理がブルーブックに関連付けられる.viewsのため、ルーティングおよびエラー処理モジュールはapp/__init__.py
の下部に導入されることに注意する.pyとerrors.pyはmain blueprintをインポートするので、ループ依存を避けるためにmainが作成されるまでルーティングとエラー処理をインポートできません.ブルーブック工場関数create_app()にプログラムに登録# app/__init__.py
def create_app(config_name):
# ...
from main import main as main_blueprint
app.register_blueprint(main_blueprint)
return app
エラーハンドラは以下の通りです.
#app/main/error.py
from flask import render_template
from . import main
@main.app_errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
@main.app_errorhandler(500)
def internal_server_error(e):
return render_template('500.html'), 500
errorhandler
モディファイヤを使用している場合、処理プログラムをトリガーできるのはブルーブックのエラーのみです.プログラムグローバルのエラー処理プログラムを登録するには、app_errorhandler
を使用してブルーブックでルーティングを定義する必要があります.# app/main/views.py
from datetime import datetime
from flask import render_template, session, redirect, url_for
from . import main
from .forms import NameForm
from .. import db
from ..models import User
@main.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
# ...
return redirect(url_for('.index'))
return render_template('index.html',
form=form, name=session.get('name'),
known=session.get('known', False),
current_time=datetime.utcnow())
ブルーブックでのビュー関数の違い
url_for()
の使い方が異なるindex()
に登録されているエンドポイント名はmain.inedx
であり、そのURLはurl_for('main.index')
を用いてプログラムを完全に修正するためのページを取得し、フォームオブジェクトもブルーブックに移動し、app/main/forms.py
モジュールに保存する7.4起動スクリプト
manage.py
ファイルプログラムの起動用#!/usr/bin/env python
import os
from app import create_app, db
from app.models import User, Role
from flask.ext.script import Manager, Shell
from flask.ext.migrate import Migrate, MigrateCommand
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
manager = Manager(app)
migrate = Migrate(app, db)
def make_shell_context():
return dict(app=app, db=db, User=User, Role=Role)
manager.add_command("shell", Shell(make_context=make_shell_context))
manager.add_command('db', MigrateCommand)
if __name__ == '__main__':
manager.run()
スクリプトは、まずアプリケーション・インスタンスを作成し、システム環境から
FLASK_CONFIG
変数を読み出し、変数が定義されていない場合はデフォルト値を使用します.次に、Flask-Script
、Flask-Migrate
、およびPython Shellに定義されたコンテキストを初期化する.7.5需要書類
pipは、次のコマンドを使用して、このファイル
pip freeze >requirements.txt
を自動的に生成することができ、別の環境でこれらの依存をインストールする準備ができたら、次のコマンドpip install -r requirements.txt
を実行します.7.6ユニットテスト
import unittest
from flask import current_app
from app import create_app, db
class BasicsTestCase(unittest.TestCase):
def setUp(self):
self.app = create_app('testing')
self.app_context = self.app.app_context()
self.app_context.push()
db.create_all()
def tearDown(self):
db.session.remove()
db.drop_all()
self.app_context.pop()
def test_app_exists(self):
self.assertFalse(current_app is None)
def test_app_is_testing(self):
self.assertTrue(current_app.config['TESTING'])
テストは典型的なユニットテストの書き方に従って構築され、実行中のプログラムと同様に、まずテスト構成を使用してプログラムを作成し、コンテキストをアクティブにします.
setUp()
およびtearDown()
メソッドは、各テストメソッドの実行前後に実行され、test_
で始まるメソッドは、テストメソッドとして実行されます.setUp()
メソッドは、テストに必要な環境を作成します.彼はまず、アプリケーションインスタンスをテストとして使用する山下文環境を作成します.これにより、テストがcurrent_app
に取得され、新しいデータベースが作成されます.データベースおよびアプリケーション・インスタンスは、tearDown()
メソッドで最後に破棄されます.最初のテストでは、アプリケーション・インスタンスが存在することを確認し、2番目のテスト・アプリケーション・インスタンスがテスト構成で実行されます.テストフォルダに正しいパッケージ構造があることを確認するために、
tests/__init__.py
ファイルを追加する必要があります.これにより、ユニットテストパッケージはテストフォルダ内のモジュールをすべてスキャンすることができます.ユニットテストを実行するためにmanage.pyにカスタムコマンドを追加し、
@manager.command
def test():
"""Run the unit tests."""
import unittest
tests = unittest.TestLoader().discover('tests')
unittest.TextTestRunner(verbosity=2).run(tests)
運転方法は以下の通りです.
(venv) $ python manage.py test
test_app_exists (test_basics.BasicsTestCase) ... ok
test_app_is_testing (test_basics.BasicsTestCase) ... ok
.----------------------------------------------------------------------
Ran 2 tests in 0.001s
OK
7.7データベースの作成
環境変数からデータベースのURLを読み込むことを優先します.また、次のコマンドを使用してデータテーブルを作成したり、最新のリビジョン
python mange.py db upgrade
にアップグレードしたりするためのデフォルトのSQLiteデータベースも用意されています.