使用 Flask 在后台任务中发送批量电子邮件
Send bulk emails in background task with Flask
我正在使用 Flask Mail 从我的 Flask 应用程序发送电子邮件。从主线程使用它没问题。但是,我有一条可以发送大量电子邮件的路由(我认为足以超过 HTTP 超时),我想快速 return 回复并 运行 在后台发送邮件.
我尝试了以下解决方案:
from concurrent.futures import ThreadPoolExecutor
executor = ThreadPoolExecutor(max_workers=1)
mailer = Mail(app) #Flask Mail Mailer
@app.route("/admin/sendmails")
def sendmails():
#
# Code to get recipients from DB here
#
executor.submit(send_links,recipients,mailer)
return "Mail sending began"
这里是 send_mails
函数的代码:
def send_links(recipients,mailing=None):
subject = "My subject"
if mailing == None : return logger.warning("No mailing available : Login links could not be sent")
with mailing.connect() as conn:
for [recipient,token,fullname] in recipients:
try:
raw_body = render_template("mails/login.txt",token=token, fullname=fullname)
html_body = render_template("mails/login.html",token=token, fullname=fullname)
msg = Message(subject,
recipients=[recipient])
msg.body = raw_body
msg.html = html_body
msg.attach('logo.png','image/png',open(os.path.join(os.path.dirname(os.path.abspath(__file__)),'static/image/logo.png'), 'rb').read(), 'inline', [['Content-ID', '<LOGO>']])
conn.send(msg)
except Exception as e:
logger.warning("Impossible to send login token \"{}\" to {}".format(token, fullname))
logger.warning(traceback.format_exc())
return True
但是 send_links
函数在 Flask 上下文中使用 render_template
(我猜邮件程序可能也使用它)所以我得到以下错误...
Traceback (most recent call last):
File "/var/www/rbt-rdd/server/utils.py", line 114, in send_links
raw_body = render_template("mails/login.txt",token=token, fullname=fullname)
File "/var/www/rbt-rdd/venv/lib/python3.9/site-packages/flask/templating.py", line 146, in render_template
ctx.app.update_template_context(context)
AttributeError: 'NoneType' object has no attribute 'app'
你知道如何解决这个问题吗?
手动推送上下文:
def send_links(recipients, mailing=None):
# ... # -
with app.app_context(): # +
... # +
参考:https://flask.palletsprojects.com/en/2.0.x/appcontext/#manually-push-a-context
您可以将其实现为装饰器,尤其是当您有许多这样的功能时:
from functools import wraps
def with_app_context(func):
@wraps(func)
def _func(*args, **kwargs):
with app.app_context():
return func(*args, **kwargs)
return _func
用法:
@with_app_context # +
def send_links(recipients, mailing=None):
...
我正在使用 Flask Mail 从我的 Flask 应用程序发送电子邮件。从主线程使用它没问题。但是,我有一条可以发送大量电子邮件的路由(我认为足以超过 HTTP 超时),我想快速 return 回复并 运行 在后台发送邮件.
我尝试了以下解决方案:
from concurrent.futures import ThreadPoolExecutor
executor = ThreadPoolExecutor(max_workers=1)
mailer = Mail(app) #Flask Mail Mailer
@app.route("/admin/sendmails")
def sendmails():
#
# Code to get recipients from DB here
#
executor.submit(send_links,recipients,mailer)
return "Mail sending began"
这里是 send_mails
函数的代码:
def send_links(recipients,mailing=None):
subject = "My subject"
if mailing == None : return logger.warning("No mailing available : Login links could not be sent")
with mailing.connect() as conn:
for [recipient,token,fullname] in recipients:
try:
raw_body = render_template("mails/login.txt",token=token, fullname=fullname)
html_body = render_template("mails/login.html",token=token, fullname=fullname)
msg = Message(subject,
recipients=[recipient])
msg.body = raw_body
msg.html = html_body
msg.attach('logo.png','image/png',open(os.path.join(os.path.dirname(os.path.abspath(__file__)),'static/image/logo.png'), 'rb').read(), 'inline', [['Content-ID', '<LOGO>']])
conn.send(msg)
except Exception as e:
logger.warning("Impossible to send login token \"{}\" to {}".format(token, fullname))
logger.warning(traceback.format_exc())
return True
但是 send_links
函数在 Flask 上下文中使用 render_template
(我猜邮件程序可能也使用它)所以我得到以下错误...
Traceback (most recent call last):
File "/var/www/rbt-rdd/server/utils.py", line 114, in send_links
raw_body = render_template("mails/login.txt",token=token, fullname=fullname)
File "/var/www/rbt-rdd/venv/lib/python3.9/site-packages/flask/templating.py", line 146, in render_template
ctx.app.update_template_context(context)
AttributeError: 'NoneType' object has no attribute 'app'
你知道如何解决这个问题吗?
手动推送上下文:
def send_links(recipients, mailing=None):
# ... # -
with app.app_context(): # +
... # +
参考:https://flask.palletsprojects.com/en/2.0.x/appcontext/#manually-push-a-context
您可以将其实现为装饰器,尤其是当您有许多这样的功能时:
from functools import wraps
def with_app_context(func):
@wraps(func)
def _func(*args, **kwargs):
with app.app_context():
return func(*args, **kwargs)
return _func
用法:
@with_app_context # +
def send_links(recipients, mailing=None):
...