Estoy usando Flask Mail para enviar correos electrónicos desde mi aplicación Flask. No hay problema para usarlo desde el hilo principal. Sin embargo, tengo una ruta que puede enviar una gran cantidad de correos electrónicos (suficientes, creo, para exceder el tiempo de espera de HTTP) y me gustaría devolver una respuesta rápidamente y ejecutar el envío de correo en segundo plano.
Probé la siguiente solución:
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" Aquí está el código para la función 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 Sin embargo, la función send_links usa render_template desde dentro del contexto Flask (y el remitente probablemente también lo use, supongo), así que aparece el siguiente 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'¿Tienes alguna idea de cómo arreglar esto?
Empuje manualmente un contexto:
def send_links(recipients, mailing=None): # ... # - with app.app_context(): # + ... # +Referencia: https://flask.palletsprojects.com/en/2.0.x/appcontext/#manually-push-a-context
Puede implementarlo como decorador, especialmente si tiene muchas funciones de este tipo:
from functools import wraps def with_app_context(func): @wraps(func) def _func(*args, **kwargs): with app.app_context(): return func(*args, **kwargs) return _funcUso:
@with_app_context # + def send_links(recipients, mailing=None): ...