Sending an email to Flask

by Alex
Sending an email to Flask
Web applications send emails all the time, and this tutorial will talk about how to add an email tool to a Flask application. The standard Python library has an smtplib module that can be used to send emails. Although the smtplib module itself is not complicated, it still requires some work. The Flask-Mail extension was created to make it easier to work with. Flask-Mail is built on top of the Python smtplib module and provides a simple interface for sending emails. It also provides bulk mailing capabilities and file attachments to messages. You can install Flask-Mail with the following command:

(env) [email protected]:~/flask_app$ pip install flask-mail

To run the extension, you need to import the Mail class from the flask_mail package and create an instance of the Mail class:

#...
from flask_mail import Mail, Message

app = Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = 'a really really really long secret key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:[email protected]/flask_app_db'

manager = Manager(app)
manager.add_command('db', MigrateCommand)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
mail = Mail(app)
#...

Next, you need to specify some configuration parameters so that Flask-Mail knows which SMTP server to connect to. To do this, you need to add the following code to the main2.py file:

#...
app.config['SECRET_KEY'] = 'a really really really long secret key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:[email protected]/flask_app_db'
app.config['MAIL_SERVER'] = 'smtp.googlemail.com'
app.config['MAIL_PORT'] = 587
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USERNAME'] = '[email protected]' # enter your email address here
app.config['MAIL_DEFAULT_SENDER'] = '[email protected]' # and here
app.config['MAIL_PASSWORD'] = ' password' # enter your password

manager = Manager(app)
manager.add_command('db', MigrateCommand)
db = SQLAlchemy(app)
mail = Mail(app)
#...

In this case Google SMTP-server is used. It’s worth noting that Gmail only allows you to send 100-150 messages per day. If this is not enough, you should consider alternatives: SendGrid or MailChimp. Instead of specifying the email and password directly in the application as was done before, it is better to store them in environment variables. That way, if the email or password changes, you won’t need to update the code. How to do this will be explained in the next lessons.

The basics of Flask-Mail

In order to compose an email, we need to create an instance of the Message class:

msg = Message('Subject', sender='[email protected]', recipients=['[email protected]'])

If you specified MAIL_DEFAULT_SENDER when setting the configuration parameters, you don’t have to pass the sender value when creating the Message instance.

msg = Message("Subject", recipients=['[email protected]'])

You must use the body attribute of the Message instance to specify the body of the message:

msg.body = "Mail body"

If it consists of HTML, you should pass it to the html attribute.

msg.html = "<p>Mail body</p>"

Finally, you can send a message by passing a Message instance to the send() method of the Mail instance:

mail.send(msg)

It’s time to test the settings by sending an email using the command line.

Sending a test message

Let’s open a terminal to type the following commands:

(env) [email protected]:~/flask_app$python main2.py shell
>>>
>>> > from main2 import mail, Message
>>> # enter your mail
>>> msg = Message("Subject", recipients=["[email protected]"])
>>> msg.html = "<h2>Email Heading</h2>n<p>Email Body</p>"
>>>
>>> mail.send(msg)
>>>

If the operation was successful, you should receive the following email with the subject “Subject”:

Email Heading
Email Body

It’s worth noting that sending via Gmail’s SMTP server will not work unless you disable two-factor authentication and allow insecure applications to access your account.

Integrating email into the app

Now when a user sends feedback, it is saved in the database, the user is notified that his message has been sent, and that’s it. But ideally, the application should notify administrators of feedback received. This can be done. Let’s open main2.py to change the contact() view function so that it sends messages:
#...
@app.route('/contact/', methods=['get', 'post'])
def contact():
   #...
	db.session.commit()
	
	msg = Message("Feedback", recipients=[app.config['MAIL_USERNAME'])
	msg.body = "You have received a new feedback from {} <{}>.".format(name, email)
	mail.send(msg)

	print("nData received. Now redirecting ...")
   #...

Next we need to start the server and go to https://localhost:5000/contact/. Let’s fill in and send the form. If everything was successful, you should receive an email. You might have noticed the delay between sending the feedback and the notification that it was sent successfully. The problem is that the mail.send() method blocks the execution of the submission function for a few seconds. As a result, the code with the page redirect won’t be executed until the mail.send() method returns. This can be solved by using threads. You can also change the sending code a bit right now. At the moment, if you want to send email anywhere else in the code, you will have to copy and paste the same lines. But you can save a few lines by encapsulating the logic of sending messages in a function. Let’s open main2.py to add the following code before the index:

#...
from threading import Thread
#...
def shell_context():
   import os, sys
   return dict(app=app, os=os, sys=sys)

manager.add_command("shell", Shell(make_context=shell_context))

def async_send_mail(app, msg):
   with app.app_context():
	mail.send(msg)


def send_mail(subject, recipient, template, **kwargs):
    msg = Message(subject, sender=app.config['MAIL_DEFAULT_SENDER'], recipients=[recipient])
    msg.html = render_template(template, **kwargs)
    thr = Thread(target=async_send_mail, args=[app, msg])
    thr.start()
   return thr

@app.route('/')
def index():
   return render_template('index.html', name='Jerry')
#...

Several changes have been made. The send_mail() function now includes all the logic to send an email. It takes a subject, a recipient and a message template. You can also give it additional arguments as keyword arguments. Why exactly in this way? The extra arguments are the data to be passed to the template. On line 17 the template is rendered, and the result is passed to the msg.html attribute. On line 18 a Thread object is created. This is done by passing the name of the function and the function arguments with which it should be called. The next line starts threads. When the thread is started, async_send_mail() is called. Now for the fun part. For the first time in the code, work is done outside the application (that is, outside the view function) in a new thread. with app.app_context(): creates the application context, and mail.send() sends the email. Next we need to create a template for the feedback message. In the templates folder you need to create a mail folder. It will store the templates for the emails. Inside the folder create a feedback.html template with the following code:

<p>You have received a new feedback from {{ name }} &lt;{{ email }}&gt </p>

Now you need to change the contact() view function to use send_mail(): After that, you need to go to https://localhost:5000/contact again , fill out the form, and send it. This time there will be no delay.

Related Posts

LEAVE A COMMENT