我正在使用 Flask-Mail 扩展从我的 Flask 应用程序发送电子邮件。它同步运行 send() 方法,我必须等到它发送消息。我怎样才能让它在后台运行?
问问题
4106 次
3 回答
9
它没有那么复杂——你需要在另一个线程中发送邮件,所以你不会阻塞主线程。但是有一个技巧。
这是我的代码,它呈现模板,创建邮件正文,并允许同步和异步发送它:
mail_sender.py
import threading
from flask import render_template, copy_current_request_context, current_app
from flask_mail import Mail, Message
mail = Mail()
def create_massege(to_email, subject, template, from_email=None, **kwargs):
if not from_email:
from_email = current_app.config['ROBOT_EMAIL']
if not to_email:
raise ValueError('Target email not defined.')
body = render_template(template, site_name=current_app.config['SITE_NAME'], **kwargs)
subject = subject.encode('utf-8')
body = body.encode('utf-8')
return Message(subject, [to_email], body, sender=from_email)
def send(to_email, subject, template, from_email=None, **kwargs):
message = create_massege(to_email, subject, template, from_email, **kwargs)
mail.send(message)
def send_async(to_email, subject, template, from_email=None, **kwargs):
message = create_massege(to_email, subject, template, from_email, **kwargs)
@copy_current_request_context
def send_message(message):
mail.send(message)
sender = threading.Thread(name='mail_sender', target=send_message, args=(message,))
sender.start()
注意@copy_current_request_context
装饰器。这是必需的,因为 Flask-Mail 内部使用请求上下文。如果我们将在新线程中运行它,则会丢失上下文。@copy_current_request_context
我们可以通过- Flask 将在调用函数时推送上下文来防止这个装饰函数。
要使用此代码,您还需要mail
使用 Flask 应用程序初始化对象:
运行.py
app = Flask('app')
mail_sender.mail.init_app(app)
于 2013-08-23T16:16:59.727 回答
2
使用Flask-Executor。完全公开,我自己写了它来解决这个确切的问题。
为什么?
- 使用 concurrent.futures 来设置线程池而不是手动管理线程可以防止创建任意数量的线程。相反,预定义的线程池用于运行从队列馈送的作业。
- Flask-Executor 为提交的任务提供当前请求上下文和当前应用程序上下文,因此您无需编写任何处理代码来管理它
这是它的样子:
from flask import Flask, current_app
from flask_executor import Executor
from flask_mail import Mail, Message
app = Flask(__name__)
# Set email server/auth configuration in app.config[]
executor = Executor(app)
mail = Mail(app)
def send_email(to, subject, message_text, message_html):
msg = Message(subject, sender=current_app.config['MAIL_USERNAME'], recipients=[to])
msg.body = message_text
msg.html = message_html
mail.send(msg)
@app.route('/signup')
def signup():
# My signup form logic
future = executor.submit(send_email, 'recipient@example.com', 'My subject', 'My text message', '<b>My HTML message</b>')
print(future.result())
return 'ok'
if __name__ == '__main__':
app.run()
基本上,您编写send_email
函数就像运行常规内联逻辑一样,并将其提交给执行程序。无论您发送多少电子邮件,只会运行执行程序中定义的最大线程数(默认为 5* 数量的 CPU 内核),并且请求中的任何溢出send_email
都会排队。
总体而言,您的代码保持简洁,您无需为要运行的每个异步函数编写一堆包装代码。
于 2018-10-13T05:16:08.480 回答
1
我想简化 Marboni 的代码,所以看看这里。
import threading
from flask import copy_current_request_context
from flask_mail import Message
from app import app, mail
def create_message(recipient, subject, body):
if not recipient:
raise ValueError('Target email not defined.')
subject = subject.encode('utf-8')
body = body.encode('utf-8')
return Message(subject, [recipient], body, sender=app.config['MAIL_USERNAME'] or "groundifly@gmail.com")
def send_async(recipient, subject, body):
message = create_message(recipient, subject, body)
@copy_current_request_context
def send_message(message):
mail.send(message)
sender = threading.Thread(name='mail_sender', target=send_message, args=(message,))
sender.start()
于 2017-11-08T16:37:36.213 回答