Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Send bulk emails in background task with Flask

I'm using Flask Mail to send emails from my Flask App. No problem using it from the main thread. However I have a route that can send a quite large amount of emails (enough I think to exceed the HTTP Timeout) and I'd like to quickly return a response and running the mail send in the background.

I tried the following solution :

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"

Here is the code for the send_mails function :

  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

However the send_links function uses render_template from within the Flask context (and the mailer probably uses it as well I guess) so I get the following error...

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'

Do you have any idea how to fix this ?

like image 524
Misfits09 Avatar asked Oct 28 '25 00:10

Misfits09


1 Answers

Manually push a context:

def send_links(recipients, mailing=None):
    # ...                    # -
    with app.app_context():  # +
        ...                  # +

Reference: https://flask.palletsprojects.com/en/2.0.x/appcontext/#manually-push-a-context


You can implement it as a decorator, especially if you have many such functions:

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

Usage:

@with_app_context  # +
def send_links(recipients, mailing=None):
    ...
like image 108
aaron Avatar answered Oct 29 '25 13:10

aaron



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!