简体   繁体   中英

Flask-Mail breaks Celery

I've got a Flask app where celery works fine and Flask-Mail on its own works fine as well.

from celery import Celery
from flask_mail import Mail, Message
app = Flask(__name__)
mail = Mail(app)

celery = Celery('main_app', 
                 broker='mongodb://localhost',
                 backend='mongodb://localhost')
@celery.task
def cel_test():
     return 'cel_test'

@app.route('/works_maybe')
def works_maybe():
    return cel_test.delay()

SO FAR, SO GOOD

cel_test works fine with the celery worker; everything shows up in mongo.

But here's where it gets weird. The "signup" plus mail method works 100% without @celery.task , but blows up when it becomes a task.

@celery.task
def send_email(some_arg, name, email):
    msg = Message(…message details..)
    return mail.send(msg)

@app.route("/signup", methods=['POST'])
def signup():
    return send_email.delay(...stuff for the message…)

THE TRACE

R = retval = fun(*args, **kwargs)
File "/Users/username/pymods/virtualenvs/directory/lib/python2.7/site-packages/celery-3.0.15-py2.7.egg/celery/task/trace.py", line 415, in __protected_call__
return self.run(*args, **kwargs)
File "/Users/username/pymods/directory/directory/main_app/main_app.py", line 43, in send_email
something = 'a string in the Message'),
File "/Users/username/pymods/virtualenvs/directory/lib/python2.7/site-packages/flask/templating.py", line 123, in render_template
ctx.app.update_template_context(context)  
AttributeError: 'NoneType' object has no attribute 'app'

Could someone explain why in one case celery works great but when I involve mail.send(msg) it breaks?

Perhaps there is something I need to learn with python more generally?

Any thoughts, if at least as to approach to this type of issue would be greatly appreciated.

Update

The bug is in the render_template portion of the send_email task.

@celery.task
def send_email(some_arg, name, email):
    msg = Message(
                  subject='hello', 
                  body=render_template('email.txt',
                  name=name, 
                  some_arg=some_arg),
                  recipients=[email]
                 )
    return mail.send(msg)

When I remove body=render_template , kablaam , it works.

I've got from flask import render_template . Perhaps render_template can't work like this?

Strangely, without Celery, the send_email plus render_template works perfect.

Hackish Success

When I force the app_context with another function everything works:

def create_email(some_arg, name, email):
    with app.test_request_context('/send_email'):
        return render_template('email.txt', 
                                name=name, 
                                some_arg=some_arg) 

and then toss it in the send_email task so

body = render_template('email.txt'…

becomes

body= create_email(some_arg, name)

And we're home free.

A lot of things done in flask are bound to the application context. For example, the render_template function, it needs to know where your application stores its templates. The session variable wants to know your application's data store or caching system. The request object, and your mail.send require some application context when being called.

If you want to call them outside the scope of your flask application, like in your celery task, do it within the app context like so:

...

with app.app_context():

    do_some_context_bound_actions()

    msg = Messgae(...)
    user_name = app.session["user"].name
    msg.html = render_template("mail/welcome.html", name=user_name)
    mail.send(msg)

...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM