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.
Table of Contents
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
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 }} <{{ email }}> </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.