【问题标题】:Flask application factory pattern with Flask-Mail使用 Flask-Mail 的 Flask 应用程序工厂模式
【发布时间】:2020-09-26 05:05:51
【问题描述】:

我有一个关于 Flask 应用程序工厂模式的简单概念性问题。

我正在尝试跨不同文件使用 Flask-Mail。我的__init__.py 文件是:

#
# __init__.py
#
from flask import Flask
from flask_pymongo import PyMongo
from flask_mail import Mail
from flask_login import LoginManager

mail = Mail()
mongo = PyMongo()
login_manager = LoginManager()

def create_app():
    app = Flask(__name__, instance_relative_config=False)
    app.config.from_object('config.DevConfig')

    mail.init_app(app)
    login_manager.init_app(app)
    mongo.init_app(app, retryWrites = False)

    with app.app_context():
        from .views import bp
        app.register_blueprint(views.bp)
    return app

还有另一个文件:

#
# views.py
#
from flask import Blueprint
from flask import current_app as app
from flask_mail import Message

from app import mongo, mail, login_manager

bp = Blueprint('bp', __name__, template_folder='templates', static_folder='static')

@bp.route('/')
def index():
    msg = Message("Success", recipients=[ email ])
    with open('template.html', 'r') as fd:
        msg.html = fd.read()
    mail.send(msg)

虽然我在我的配置文件中设置了MAIL_DEFAULT_SENDER,但在点击views.py 中的mail.send(msg) 行时,我收到了没有指定默认发件人的错误。检查mail 对象后,我看到它没有设置配置变量。

Per this tutorial,似乎我需要在此模式下使用mail 对象时手动设置current_app.config['MAIL_DEFAULT_SENDER'],并且需要在mail 对象周围编写一个额外的with app.app_context(): 块,这样它是用正确的配置变量实例化的。

这似乎需要做很多额外的工作,那么有没有另一种方法可以直接获取在 create_app 中初始化并设置了所有正确配置变量的 mail 对象?

感谢您的帮助!

编辑:

创建extensions.py:

from flask_pymongo import PyMongo
from flask_mail import Mail
from flask_login import LoginManager

mail = Mail()
mongo = PyMongo()
login_manager = LoginManager()

并将__init__.py修改为:

from flask import Flask

def create_app():
    app = Flask(__name__, instance_relative_config=False)
    app.config.from_object('config.DevConfig')

    from app.extensions import mail, login_manager, mongo
    mail.init_app(app)
    login_manager.init_app(app)
    mongo.init_app(app, retryWrites = False)

    with app.app_context():
        from .views import bp
        app.register_blueprint(views.bp)
    return app

对于views.py,我有:

from flask import Blueprint
from flask import current_app
from flask_mail import Message

from app.extensions import mongo, mail, login_manager

bp = Blueprint('bp', __name__, template_folder='templates', static_folder='static')

@bp.route('/')
def index():
    msg = Message("Success", recipients=[ email ])
    with open('template.html', 'r') as fd:
        msg.html = fd.read()
    mail.send(msg)

【问题讨论】:

    标签: python flask flask-mail


    【解决方案1】:

    这是一个导入问题。请阅读official documentation about Application Factories & Extensions

    说明:

    1. __init__.py 被加载,然后 mail 对象被创建。

    2. 在某个地方,调用了 create_app,然后创建了一个新的 app 对象并使用之前创建的 mail 对象。 p>

    3. 在处理 create_app 时,会导入蓝图。 (为什么需要在 app_context 中加载蓝图?)

    4. 在导入蓝图时,它会导入from app [...], mail。导入不会在模块之间共享(在这种情况下是肯定的)。因此__init__.py 在导入时再次执行并创建了一个未配置的新mail 对象。 (如果您使用调试器,您会看到有 2 个邮件对象。)

    最后,您有 2 个 mail 对象:一个已配置,一个未配置

    如何处理:

    如文档中所述:

    最好创建您的扩展程序和应用程序工厂,以便扩展程序对象最初不会绑定到应用程序。

    他们举了以下例子:

    你不应该这样做:

    def create_app(config_filename):
        app = Flask(__name__)
        app.config.from_pyfile(config_filename)
    
        db = SQLAlchemy(app)
    

    但是,更确切地说,在 model.py(或等效)中:

    db = SQLAlchemy()
    

    并在您的 application.py(或等效项)中:

    def create_app(config_filename):
       app = Flask(__name__)
       app.config.from_pyfile(config_filename)
    
       from yourapplication.model import db
       db.init_app(app)
    

    最后,您应该在另一个文件中创建您的 mail 对象并将其导入您的__init__.py。如果在这个蓝图中只使用mail对象,可以直接在这个蓝图中声明。

    您可以找到一个完整的example: Flaskr (Official example),它在使用 Flask-Mail 时使用 DB 扩展。

    为什么会如此混乱?您可以在 Google 上找到的大多数示例不使用应用程序工厂,并且始终位于一个文件中。在他们的情况下,您将在同一个文件中声明所有内容。在您的情况下,您将在模块之间共享对象,因此它们应该可以正确访问。

    【讨论】:

    • 非常感谢您的回复!我将所有内容都移到了一个单独的文件 (extensions.py) 中,如对原始问题的编辑中所指定,但我仍然无法正确导入 mail。对上述变化有何想法?
    • 你得到什么错误?添加堆栈跟踪可能很有用。
    • 不幸的是我没有收到任何错误,电子邮件只是无法静默发送。我开始认为这是 Flask-Mail 特有的,因为 Flask-Mongo 和 Flask-Login 可以正常使用这种模式。
    • 我帮助你处理了邮件对象,但是在那之后,没有错误日志,我不能再做任何事情了。
    猜你喜欢
    • 1970-01-01
    • 2014-06-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-17
    • 2020-06-11
    • 2019-11-07
    相关资源
    最近更新 更多