Email

Chapter 6. Email

Many types of applications need to notify users when certain events occur, and the usual method of communication is email. In this chapter you are going to learn how to send emails from a Flask application.

Email Support with Flask-Mail

Although the smtplib package from the Python standard library can be used to send email inside a Flask application, the Flask-Mail extension wraps smtplib and integrates it nicely with Flask. Flask-Mail is installed with pip:

The extension connects to a Simple Mail Transfer Protocol (SMTP) server and passes emails to it for delivery. If no configuration is given, Flask-Mail connects to localhost at port 25 and sends email without authentication. Table 6-1 shows the list of configuration keys that can be used to configure the SMTP server.

Key Default Description
MAIL_SERVER localhost Hostname or IP address of the email server
MAIL_PORT 25 Port of the email server
MAIL_USE_TLS False Enable Transport Layer Security (TLS) security
MAIL_USE_SSL False Enable Secure Sockets Layer (SSL) security
MAIL_USERNAME None Mail account username
MAIL_PASSWORD None Mail account password

During development it may be more convenient to connect to an external SMTP server. As an example, Example 6-1 shows how to configure the application to send email through a Google Gmail account.

Tip

Never write account credentials directly in your scripts, particularly if you plan to release your work as open source. To protect your account information, have your script import sensitive information from environment variables.

Note

For security reasons, Gmail accounts are configured to require external applications to use OAuth2 authentication to connect to the email server. Unfortunately, Python’s smtplib library does not support this method of authentication. To make your Gmail account accept standard SMTP authentication, go to your Google account settings page and select “Signing in to Google” from the left menu bar. On that page, locate the “Allow less secure apps” setting and make sure it is enabled. If enabling this setting on your personal Gmail account concerns you, create a secondary account only to test sending emails.

Flask-Mail is initialized as shown in Example 6-2.

The two environment variables that hold the email server username and password need to be defined in the environment. If you are on Linux or macOS, you can set these variables as follows:

For Microsoft Windows users, the environment variables are set as follows:

Sending Email from the Python Shell

To test the configuration, you can start a shell session and send a test email (replace you@example.com with your own email address):

Note that Flask-Mail’s send() function uses current_app, so it needs to be executed with an activated application context.

Integrating Emails with the Application

To avoid having to create email messages manually every time, it is a good idea to abstract the common parts of the application’s email sending functionality into a function. As an added benefit, this function can render email bodies from Jinja2 templates to have the most flexibility. The implementation is shown in Example 6-3.

The function relies on two application-specific configuration keys that define a prefix string for the subject and the address that will be used as the sender. The send_email() function takes the destination address, a subject line, a template for the email body, and a list of keyword arguments. The template name must be given without the extension, so that two versions of the template can be used for the plain text and HTML bodies. The keyword arguments passed by the caller are given to render_template() so that they can be used by the templates that generate the email body as template variables.

The index() view function can easily be expanded to send an email to the administrator whenever a new name is received with the form. Example 6-4 shows this change.

The recipient of the email is given in the FLASKY_ADMIN environment variable that’s loaded into a configuration variable of the same name during startup. Two template files need to be created for the text and HTML versions of the email. These files are stored in a mail subdirectory inside templates to keep them separate from regular templates. The email templates expect the user to be given as a template argument, so the call to send_email() includes it as a keyword argument.

Tip

If you have cloned the application’s Git repository on GitHub, you can run git checkout 6a to check out this version of the application.

In addition to the MAIL_USERNAME and MAIL_PASSWORD environment variables described earlier, this version of the application needs the FLASKY_ADMIN environment variable. For Linux and macOS users, the command to set this variable is:

For Microsoft Windows users, this is the equivalent command:

With these environment variables set, you can test the application and receive an email every time you enter a new name in the form.

Sending Asynchronous Email

If you sent a few test emails, you likely noticed that the mail.send() function blocks for a few seconds while the email is sent, making the browser look unresponsive during that time. To avoid unnecessary delays during request handling, the email send function can be moved to a background thread. Example 6-5 shows this change.

This implementation highlights an interesting problem. Many Flask extensions operate under the assumption that there are active application and/or request contexts. As mentioned previously, Flask-Mail’s send() function uses current_app, so it requires the application context to be active. But since contexts are associated with a thread, when the mail.send() function executes in a different thread it needs the application context to be created artificially using app.app_context(). The app instance is passed to the thread as an argument so that a context can be created.

Tip

If you have cloned the application’s Git repository on GitHub, you can run git checkout 6b to check out this version of the application.

If you run the application now, you will notice that it is much more responsive, but keep in mind that for applications that send a large volume of email, having a job dedicated to sending email is more appropriate than starting a new thread for each email send operation. For example, the execution of the send_async_email() function can be sent to a Celery task queue.

This chapter completes the overview of the features that are a must-have for most web applications. The problem now is that the hello.py script is starting to get large, and that makes it harder to work with. In the next chapter, you will learn how to structure a larger application.

Table of contents collapsed

Powered by Forestry.md