Flask-Mail导致Celery出错

3 投票
2 回答
2891 浏览
提问于 2025-04-17 16:50

我有一个Flask应用,celery运行得很好,Flask-Mail单独使用也没问题。

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()

到目前为止,一切正常

cel_test在celery工作进程中运行得很好;所有数据都能在mongo中显示出来。

但是事情开始变得奇怪了。“注册”加上发送邮件的方法在没有@celery.task的情况下运行得100%正常,但一旦变成任务就出问题了。

@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…)

错误追踪

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'

有人能解释一下为什么在一种情况下celery运行得很好,但当我涉及到mail.send(msg)时就出错了吗?

也许我需要更深入地学习一下Python的相关知识?

如果能给出一些建议,尤其是如何处理这类问题,我将非常感激。

2 个回答

0

更新

这个问题出在 send_email 任务中的 render_template 部分。

@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)

当我去掉 body=render_template 的时候,哇哦,它就能正常工作了。

我已经引入了 from flask import render_template。也许 render_template 不能这样使用?

奇怪的是,没有使用 Celery 的时候,send_emailrender_template 可以完美运行。

巧妙的解决办法

当我通过另一个函数强制使用 app_context 时,一切都正常了:

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) 

然后把它放进 send_email 任务里,这样

body = render_template('email.txt'…

就变成了

body= create_email(some_arg, name)

这样我们就没问题了。

1

在Flask中,很多操作都和应用的上下文有关。比如,render_template这个函数需要知道你的应用在哪里存放模板文件。session变量则想要了解你的应用使用的数据存储或缓存系统。还有request对象和mail.send,在调用时也需要一些应用上下文的信息。

如果你想在Flask应用的范围之外调用这些,比如在你的celery任务中,就需要在应用上下文中进行调用,方法如下:

...

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)

...

撰写回答