Run Flask-Mail asynchronously
email, flask, python
Solution
It's not so complex - you need to send mail in another thread, so you will not block the main thread. But there is one trick.
Here is my code that renders template, creating mail body, and allows to send it both synchronously and asynchronously:
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()
Pay your attention to `@copy_current_request_context` decorator. It's required because Flask-Mail inside uses request context. If we will run it in the new thread, context will be missed. We can prevent this decorating function with `@copy_current_request_context` - Flask will push context when function will be called.
To use this code you also need to initialize `mail` object with your Flask application:
run.py
app = Flask('app')
mail_sender.mail.init_app(app)
Problem
I am sending emails from my Flask app with Flask-Mail extension. It runs send() method synchronously and I have to wait until it sends the message. How can I make it run in background?