Skip to content
This repository has been archived by the owner on Feb 22, 2024. It is now read-only.

Document send_mail_task #194

Closed
nickretallack opened this issue Dec 12, 2013 · 11 comments
Closed

Document send_mail_task #194

nickretallack opened this issue Dec 12, 2013 · 11 comments

Comments

@nickretallack
Copy link
Contributor

I was hoping to make my app faster at sending confirmation emails when I noticed this in the code. Could you add a bit of explanation as to how to use this? I assume it has something to do with celery.

@eriktaubeneck
Copy link

You likely want to use this. If you are using celery, you could create a task with this snippet:

from app import celery #import from where you create your celery object
from flask.ext.security import confirmable

@celery.task()
def send_email_confirmation(user):
    confirmable.send_confirmation_instructions(user)

To speed up the send time, you'll want to dedicate more resources to that task. The celery docs are quite useful to help you figure out what to do for your setup.

@nickretallack
Copy link
Contributor Author

Oh, I just create the task myself? I was assuming there was some purpose to the code I pointed at.

@eriktaubeneck
Copy link

So the function that contains the line you point to is just a function for sending mail arbitrarily using Flask-Security. The line you points to refers to an attribute on the base security object you can override the function which actually does the mail sending. This would be useful, for example, with the above use case with celery. Without the attribute set, you run

mail = current_app.extensions.get('mail')
mail.send(msg)

you could change this by somewhere using a decorator as it is done in the tests. This would allow you to instead call mail._send.delay(msg), if you created such a task.

This would be an alternate setup to the one proposed above.

@mattupstate
Copy link
Collaborator

Hey guys, if you want to use Celery to send Flask-Security emails, you'll do something like this (pseudo code):

from celery import Celery
from flask import Flask
from flask_security import Security, .. datastore stuff ..
from flask_mail import Mail

celery = Celery()
app = Flask(__name__)
mail = Mail(app)
security = Security(app, .. datastore stuff ..)

@celery.task
def send_flask_mail(msg):
    mail.send(msg)

@security.send_mail_task
def delay_flask_security_mail(msg):
    send_flask_mail.delay(msg)

@erwagasore
Copy link

from app.factory import create_celery_app, mail, security

celery = create_celery_app()

@celery.task()
def send_security_mail(msg):
    mail.send(msg)

@security.send_mail_task
def delay_security_mail(msg):
    send_security_mail.delay(msg)

@mattupstate, based on the code above I am getting a RuntimeError: maximum recursion depth exceeded when I run celery celery worker -A app.tasks -l INFO

@joaobarbosa
Copy link

@erwagasore did you managed to fix it? I'm having the same issue 😢

@eriktaubeneck
Copy link

I think this is likely the same issue described in issue 340.

@joaobarbosa
Copy link

Thanks @eriktaubeneck!

@jxltom
Copy link
Contributor

jxltom commented Feb 9, 2017

@erwagasore @joaobarbosa @eriktaubeneck

If you are going to use factory method, recursion error will raise if it is not used properly. Here is the example based on author's example for reference. The trick is using the state object returned by init_app for setting cutomized mail sending function. The author has explained it here and here in #141.

from celery import Celery
from flask import Flask
from flask_security import Security, .. datastore stuff ..
from flask_mail import Mail

celery = Celery()
mail = Mail()
security = Security()

def create_app():
    """Initialize Flask instance."""
    app = Flask(__name__)
    mail.init_app(app)
    security_ctx = security.init_app(app,..datastore stuff..)

    @celery.task
    def send_flask_mail(msg):
        mail.send(msg)

    # Flexible way for define custom mail sending function.
    @security_ctx.send_mail_task
    def delay_flask_security_mail(msg):
        send_flask_mail.delay(msg)

    # You can also define like this as shortcurt
    security_ctx.send_mail_task(send_flask_mail.delay)

    return app

But it looks like the default serializer is JSON (at least in Celery 4.0) and flask_mail.Message can not be jsonified and then passed into Celery. So actually your custom email sending function might be like this:

from flask_mail import Message

@celery.task
def send_flask_mail(**kwargs):
        mail.send(Message(**kwargs))

@security_ctx.send_mail_task
def delay_flask_security_mail(msg):
    send_flask_mail.delay(subject=msg.subject, sender=msg.sender,
                          recipients=msg.recipients, body=msg.body,
                          html=msg.html)

@jirikuncar
Copy link
Collaborator

@jxltom we are using similar solution as you have presented in #194 (comment). Would you mind extending the documentation with your example?

@jxltom
Copy link
Contributor

jxltom commented Feb 10, 2017

@jirikuncar Sure, will do that.

galeo pushed a commit to galeo/flask-security-outdated that referenced this issue Mar 4, 2020
* Run tests under Python 3.8

* Require pony version 0.7.11 (First to work in python 3.8)

* Upgrade minimum psycopg2 version to work under Python 3.8

* Upgrade minimum coverage version to work under Python 3.8
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

No branches or pull requests

7 participants