My flask project is based on Flask-Cookiecutter and I need to send emails asynchronously.
Function for sending email was configured by Miguel’s Tutorial and sending sync
You can move app = Flask(__name__)
out of the application factory and place it at the module level. This allows you to pass the app instance with it's application context into your thread for sending the email. You'll likely need to change some imports in other areas to prevent circular dependencies, but it shouldn't be too bad.
Here is an example of how you can do this using Flask-Mail and Flask-RESTful. It also shows how to use pytest for testing this.
from flask import Flask
from .extensions import mail
from .endpoints import register_endpoints
from .settings import ProdConfig
# app context needs to be accessible at the module level
# for the send_message.send_
app = Flask(__name__)
def create_app(config=ProdConfig):
""" configures and returns the the flask app """
app.config.from_object(config)
register_extensions()
register_endpoints(app)
return app
def register_extensions():
""" connects flask extensions to the app """
mail.init_app(app)
And in your module for sending emails you would have something like this:
from flask_mail import Message
from app import app
from app import mail
from utils.decorators import async_task
def send_email(subject, sender, recipients, text_body, html_body=None, **kwargs):
app.logger.info("send_email(subject='{subject}', recipients=['{recp}'], text_body='{txt}')".format(sender=sender, subject=subject, recp=recipients, txt=text_body))
msg = Message(subject, sender=sender, recipients=recipients, **kwargs)
msg.body = text_body
msg.html = html_body
app.logger.info("Message(to=[{m.recipients}], from='{m.sender}')".format(m=msg))
_send_async_email(app, msg)
@async_task
def _send_async_email(flask_app, msg):
""" Sends an send_email asynchronously
Args:
flask_app (flask.Flask): Current flask instance
msg (Message): Message to send
Returns:
None
"""
with flask_app.app_context():
mail.send(msg)
Note: I posted this years ago and I feel instantiating the flask object outside of the application factory is not ideal. The send_email
function will need a flask instance to work, but you can instantiate a new flask app in the function (don't forget your config).
I would guess that current_app
may also work but I feel that might have side effects given that it would need to create a new app context with-in a current app context, seems wrong, but might work.
A good option to consider: Look into celery and use RabbitMQ for your backend.
For larger apps or volumes you may look into decoupling the mailing of emails into a different app via message broker like RabbitMQ. Look into Event Driven Design patterns. This may be attractive if you have multiple apps that will need a mailing service. This could be nice if your service needs to support audit logs, delivery recovery, etc.