简体   繁体   English

Flask 应用工厂模式与 Flask-Mail

[英]Flask application factory pattern with Flask-Mail

I have a simple conceptual question about the Flask application factory pattern.我有一个关于 Flask 应用程序工厂模式的简单概念问题。

I'm trying to use Flask-Mail across different files.我正在尝试在不同的文件中使用Flask-Mail My __init__.py file is:我的__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

And the other file:另一个文件:

#
# 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)

Though I set MAIL_DEFAULT_SENDER in my config file, I'm getting the error that there is no default sender specified when hitting the mail.send(msg) line in views.py .虽然我在我的配置文件中设置MAIL_DEFAULT_SENDER ,但我收到错误消息,即在views.py中点击mail.send(msg)行时没有指定默认发件人。 After checking the mail object, I saw that it had no config variables set.检查mail object 后,我看到它没有设置配置变量。

Per this tutorial , it seemed that I'd need to manually set current_app.config['MAIL_DEFAULT_SENDER'] whenever using the mail object under this pattern, and would need to write an additional with app.app_context(): block around the mail object such that it was instantiated with the proper config variables. 根据本教程,似乎我需要在此模式下使用mail object 时手动设置current_app.config['MAIL_DEFAULT_SENDER'] ,并且需要with app.app_context():阻止mail object这样它就被正确的配置变量实例化了。

This seems like a lot of extra work, so is there another way to directly get the mail object that was initialized in create_app with all the proper config variables set?这似乎需要做很多额外的工作,那么有没有另一种方法可以直接获取在create_app中初始化并设置了所有正确配置变量的mail object?

Appreciate the help with this!感谢您的帮助!

EDIT:编辑:

Created extensions.py :创建extensions.py

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

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

And modified __init__.py to be:并将__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

And for views.py , I have:对于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)

It is an import problem.这是一个进口问题。 Please read the official documentation about Application Factories & Extensions .请阅读有关 Application Factories & Extensions 的官方文档

Explanations:说明:

  1. __init__.py is loaded, then mail object is created. __init__.py被加载,然后邮件object 被创建。

  2. Somewhere, create_app is called, then a new app object is created and uses the mail object previously created.在某处调用create_app ,然后创建一个新应用object 并使用之前创建的邮件object。

  3. While processing the create_app , blueprints are imported.在处理create_app时,会导入蓝图。 (Why do you need load blueprints within the app_context ?) (为什么需要在app_context中加载蓝图?)

  4. While blueprint is imported, it imports from app [...], mail .在导入蓝图时,它from app [...], mail导入。 Imports are not shared among modules (in this case for sure).导入不会在模块之间共享(在这种情况下是肯定的)。 So __init__.py is executed again while being imported and created a new mail object that is not configured.所以__init__.py在导入时再次执行,并创建了一个未配置的新邮件object。 (If you use a debugger you will see that there are 2 mail objects.) (如果您使用调试器,您会看到有 2 个邮件对象。)

In the end, you have 2 mail object: one is configured and one is not .最后,您有 2封邮件object:一封已配置,一封未配置

How to handle that:如何处理:

As written in the documentation:如文档中所述:

It's preferable to create your extensions and app factories so that the extension object does not initially get bound to the application.最好创建您的扩展和应用程序工厂,以便扩展 object 最初不会绑定到应用程序。

And they give the following example:他们给出了以下示例:

You should not do:你不应该这样做:

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

But, rather, in model.py (or equivalent):但是,更确切地说,在 model.py (或等效)中:

 db = SQLAlchemy()

and in your application.py (or equivalent):并在您的 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)

Finally, you should create your mail object in a different file and import it your __init__.py .最后,您应该在不同的文件中创建您的邮件object 并将其导入您的__init__.py If you only use the mail object in this blueprint, it could be declared directly in this blueprint.如果本蓝图中只使用邮件object,可以直接在本蓝图中声明。

You can find a full example: Flaskr (Official example) that uses a DB extension as you use Flask-Mail.您可以找到一个完整的示例:Flaskr(官方示例) ,它在您使用 Flask-Mail 时使用 DB 扩展。

Why is it so confusing?为什么会如此混乱? Most of the examples you can find on Google, do not use application factories and are always in one file.您可以在 Google 上找到的大多数示例不使用应用程序工厂,并且始终在一个文件中。 In their case, you will declare everything in the same file.在他们的情况下,您将在同一个文件中声明所有内容。 In your case, you will share objects among modules, so they should be accessible correctly.在您的情况下,您将在模块之间共享对象,因此它们应该可以正确访问。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM