zds
zds

Reputation: 964

Want to decorate email sending with new thread

I am stuck on decorating the function. Help me find out the problem. Decorator:

def sync(f):
    def wrapper(*args, **kwargs):
        thr = Thread(target=f, args=args, kwargs=kwargs)
        thr.start()
    return wrapper

Sending functions:

def send_email(subject, sender, recipients, text_body, html_body):
    """ Build and send e-mail with the provided parameters."""

    msg = Message(subject, sender=sender, recipients=recipients)
    msg.body = text_body
    msg.html = html_body
    mail.send(msg)

@sync
def send_confirmation_email(recipient, app = current_app):
    """Send registration email to the provided recipient."""

    subject = 'Account confirmation e-mail'
    sender = '[email protected]'
    recipients = [recipient]
    activation, deletion = confirmation_links(recipient) #construct hashed links
    text_body = render_template('confirmation_email.txt',
                            activation = activation,
                            deletion = deletion)
    html_body = render_template('confirmation_email.html',
                            activation = activation,
                            deletion = deletion)
    send_email(subject, sender, recipients, text_body, html_body)

Views function:

@app.route('/signup', methods = ['GET', 'POST'])
def signup():
    form = SignupForm()
    if form.validate_on_submit():
        send_confirmation_email(recipient = form.email.data)
        return redirect(url_for('activate'))
    else:
        return render_template('signup.html',
                       title = 'Sign Up',
                       form = form)

The problem is that emails are not sent with active decorator @sync. When I remove @sync decorator everything is working but, of course, without threading. And app waits a few seconds before redirect. Any help will be helpful. Thanks.

Upvotes: 0

Views: 538

Answers (1)

Jeremy Allen
Jeremy Allen

Reputation: 6644

The render_template function requires a Flask application context to work.

When your send_confirmation_email function is run in a seperate thread there is no application context set up for render_template to work.

You could solve this by using app.app_context():

@sync
def send_confirmation_email(recipient):
    """Send registration email to the provided recipient."""

    with app.app_context():
        subject = 'Account confirmation e-mail'
        sender = '[email protected]'
        recipients = [recipient]
        activation, deletion = confirmation_links(recipient) #construct hashed links
        text_body = render_template('confirmation_email.txt',
                                activation = activation,
                                deletion = deletion)
        html_body = render_template('confirmation_email.html',
                                activation = activation,
                                deletion = deletion)
        send_email(subject, sender, recipients, text_body, html_body)

You could also look into using something like Celery

Upvotes: 2

Related Questions