Flask 应用程序中的电子邮件组件无法访问应用程序工厂模式中的应用程序实例

问题描述 投票:0回答:1

我正在练习烧瓶。我制作了一个简单的 Flask 应用程序,它向管理员发送一封电子邮件,说“新用户已加入”。一旦有人输入自己的名字并在表格上提交。

这是电子邮件组件:

from flask import current_app, render_template
from flask_mail import Message
from threading import Thread
from app import mail


# Send aync email functions
def send_aysnc_email(app, msg):
    with app.app_context():
        mail.send(msg)


def send_mail(to, subject, template, **kwargs):
    app = current_app
    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_aysnc_email, args=[app, msg])
    thr.start()
    return thr

当我运行应用程序并在表单上输入姓名并按提交时,电子邮件不会发送。

我在日志中收到一条错误消息

Exception in thread Thread-3 (send_aysnc_email):
Traceback (most recent call last):
  File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
127.0.0.1 - - [02/Sep/2023 14:39:29] "POST / HTTP/1.1" 302 -
    self.run()
  File "/usr/lib/python3.10/threading.py", line 953, in run
    self._target(*self._args, **self._kwargs)
  File "/home/cadbay53/Desktop/practice/flask/app/emails.py", line 9, in send_aysnc_email
    with app.app_context():
  File "/home/cadbay53/Desktop/practice/flask/venv/lib/python3.10/site-packages/werkzeug/local.py", line 311, in __get__
    obj = instance._get_current_object()
  File "/home/cadbay53/Desktop/practice/flask/venv/lib/python3.10/site-packages/werkzeug/local.py", line 508, in _get_current_object
    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.

我找到了解决此问题的方法,但我不明白为什么当前代码不起作用。

这是我的应用程序工厂:

from flask import Flask, render_template
from flask_bootstrap import Bootstrap
from flask_mail import Mail
from flask_moment import Moment
from flask_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)

    from .main import main as main_blueprint

    app.register_blueprint(main_blueprint)

    return app

这是我创建应用程序实例的文件:

import os
from app import create_app
from app.models import User, Role
from flask_migrate import Migrate
from app import db

app = create_app("default")
migrate = Migrate(app, db)


@app.shell_context_processor
def make_shell_context():
    return dict(db=db, User=User, Role=Role)


@app.cli.command()
def test():
    """Run unit tests."""
    import unittest

    tests = unittest.TestLoader().discover("tests")
    unittest.TextTestRunner(verbosity=2).run(tests)

这些是配置:

import os

basedir = os.path.abspath(os.path.dirname(__file__))


class Config:
    SECRET_KEY = "***"
    MAIL_SERVER = "smtp.gmail.com"
    MAIL_PORT = 587
    MAIL_USE_TLS = True
    MAIL_USERNAME = "***@***.com"
    MAIL_PASSWORD = "****"
    FLASKY_MAIL_SUBJECT_PREFIX = "[Flasky]"
    FLASKY_MAIL_SENDER = "Flasky Admin <***@***.com>"
    FLASKY_ADMIN = "***@***.com"
    SQLALCHEMY_TRACK_MODIFICATIONS = False

    @staticmethod
    def init_app(app):
        pass


class DevelopmentConfig(Config):
    DEBUG = True
    SQLALCHEMY_DATABASE_URI = "sqlite:///" + os.path.join(basedir, "data-dev.sqlite")


class TestConfig(Config):
    TESTING = True
    SQLALCHEMY_DATABASE_URI = "sqlite:///"


class ProductionConfig(Config):
    SQLALCHEMY_DATABASE_URI = "sqlite:///" + os.path.join(basedir, "data.sqlite")


config = {
    "development": DevelopmentConfig,
    "testing": TestConfig,
    "production": ProductionConfig,
    "default": DevelopmentConfig,
}

这是我的工作:

# Send aync email functions
def send_aysnc_email(app, msg):
    with app.app_context():
        mail.send(msg)


def send_mail(to, subject, template, **kwargs):
    app = current_app._get_current_object()
    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_aysnc_email, args=[app, msg])
    thr.start()
    return thr

我不明白为什么当提供当前应用程序作为参数并且与

app.app_context()
配合使用时,
current_app._get_current_object()
无法访问当前应用程序实例。

python flask python-multithreading python-3.10 flask-mail
1个回答
0
投票

current_app
是Flask提供的代理对象,动态解析为当前Flask应用上下文。

使用

current_app._get_current_object()
是一种有效的技术,可以在单独的线程中访问当前应用程序实例,而无需建立新的应用程序上下文。这种方法可以有效地检索应用程序实例,而不会破坏现有的应用程序上下文。

© www.soinside.com 2019 - 2024. All rights reserved.