Send Email From Flask With Flask-Mail and Jinja Templates
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo set up the project on your local machine, please follow the directions provided in the README.md
file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.
Sending emails
Now that we support user registration, it would be nice to send emails to confirm their registration. In the future, sending email will be important to send receipts to purchasers and other notifications.
Flask does not have a opinionated way to send emails, but we can re-use some of the same primitives that Flask comes with like Jinja and render_template
. To actually send out email, we will need to communicate with an external email server that will actually send out an email. The Flask-Mail
library will help us manage communicating to our email server over a protocol known as SMTP (Simple Mail Transfer Protocol).
Why use an external mail server?A big reason to use an external provider is to reduce the chance that your email will be sent to a spam filter. Services like GMail can trust a large external email vendor IP addresses but are less likely to trust mail coming from a random IP address that has never sent email to them before. In addition, some cloud providers and internet providers disallow sending out emails over SMTP.
Configuration
Install flask_mail
Add flask_mail
to your requirements.txt file and then (in your virtual environment) run, pip install -r requirements.txt
To initialize the flask_mail
extension, we'll follow the same steps we have before.
First, we will initialize the extension in yumroad/extensions.py
.
from flask_mail import Mail
...
mail = Mail()
Then we'll import that object in yumroad/__init__.py
and call the init_app
method to pass in our application configuration.
from yumroad.extensions import (csrf, db, migrate, mail, login_manager)
...
def create_app(environment_name='dev'):
...
mail.init_app(app)
Sign up with a email sender
We will need to work with a third party mail provider to send out emails from our application. I'd reccomend using Mailgun. Their service includes a test domain from which you can send emails to a set of pre-registered addresses.
Go to Mailgun.org and sign up for a free account, which will allow you to send a few hundred emails per month for free. The cost from there is pay as you go if you plan on sending a lot more than a few hundred a month.
You can also use other providers like Postmark, Sendgrid, Mailjet, or AWS Simple Email Service. Regardless of which provider you get, you should be able to get STMP API credentials.
Once you have the credentials, we will need to tell our Flask application how to connect to the service. Flask-Mail
looks at the following Flask configuration variables (MAIL_SERVER
, MAIL_PORT
).
For now, we'll add these under the BaseConfiguration
, which will make these configuration settings accessible in each our environments (production, test, and development). Using os.getenv
will allow us to read from the environment variables, and use a default if it's not set. It's not a good idea to let our actual credentials live in this file because it might be tracked in any version control you are using, or could easily be leaked if someone gets access to your source code.
import os
class BaseConfig:
...
MAIL_SERVER = os.getenv('MAIL_SERVER', 'smtp.mailgun.org')
MAIL_PORT = os.getenv('MAIL_SERVER_PORT', 2525)
MAIL_USERNAME = os.getenv('MAIL_USERNAME')
MAIL_USE_TLS = os.getenv('MAIL_USE_TLS', True)
MAIL_PASSWORD = os.getenv('MAIL_PASSWORD')
The configuration above, works for Mailgun, but change the default port, server address, or TLS configuration to match the connection instructions provided from the service you are using. Flask-Mail
provides some addition configuration settings in the documentation.
Now that Flask will lookup the credentials to send emails from our environment variables, we will need set the environment variables to the values we got from our email provider. One way we can do that is to directly prefix the flask run
command with those, similar to how we used to set FLASK_ENV
.
$ MAIL_USERNAME='[email protected]' MAIL_PASSWORD='abc123' flask run
While this would work, it could be unwieldy to type. For now, we'll take the path of flask run
.
# One time setup per session
export FLASK_ENV='development'
export FLASK_APP='yumroad:create_app'
export MAIL_USERNAME='[email protected]'
export MAIL_PASSWORD='abc123'
flask run
Another approach is to create an executable bash script called dev.sh
that runs the following script.
#!/bin/sh
FLASK_ENV='development' FLASK_APP='yumroad:create_app' MAIL_USERNAME='[email protected]' MAIL_PASSWORD='abc123' flask run
Then once your virtual environment is activated, you only need to run ./dev.sh
to launch a development server. If you're using a version tracking system, be sure not to commit this file into your version tracking system
If you have a lot of environment variables, look into using a library called
python-dotenv
to use files to store environment
Sending basic email
To send an email, we'll need to tell Flask-Mail
what we want our our email to look like first.
The Message
class from Flask-Mail
allows us to specify all of the fields we'll need to set for an email, like the subject, recipients, and body.
To create a simple email, with one recipient, and from [email protected]
, we'd pass in our subject, sender, recipients, and message body.
from flask_mail import Message
msg = Message("Our Subject",
sender="[email protected]",
recipients=["[email protected]"],
body="Our content")
To actually send out the email, we'd pass the message instance into the send
method from the Mail instance we just configured with our Flask application.
from yumroad.extensions import mail
mail.send(msg)
To make our messages stand out, we'll want to have the sender's actual name appear in our inbox instead of [email protected]
. To do that we can use the formatting of "Sender Name <[email protected]>"
, or more simply, pass in a tuple to the recipients
argument of message.
msg = Message("Our Subject",
sender=("Sender Name", "[email protected]"),
recipients=["[email protected]"],
body="Our content")
Since we want to send these out when a user signs up, let's create a function that we can call when a user registers.
In order to keep the logic within blueprints simple and make emails testable by themselves, we'll create a separate email.py
module within the yumroad
folder. Our first email will be an email to thank a user for registering.
from flask_mail import Message
from yumroad.extensions import mail
DEFAULT_FROM = ('Yumroad', '[email protected]')
def send_basic_welcome_message(recipient_email):
message = Message('Welcome to yumroad',
sender=DEFAULT_FROM,
recipients=[recipient_email],
body="Thanks for joining. Let us know if you have any questions!")
mail.send(message)
from flask_mail import Message
from yumroad.extensions import mail
DEFAULT_FROM = ('Yumroad', '[email protected]')
def send_basic_welcome_message(recipient_email):
message = Message('Welcome to yumroad',
sender=DEFAULT_FROM,
recipients=[recipient_email],
body="Thanks for joining. Let us know if you have any questions!")
mail.send(message)
Now in our controller, we should call this function after we've created a User
and Store
This lesson preview is part of the Fullstack Flask: Build a Complete SaaS App with Flask course and can be unlocked immediately with a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Fullstack Flask: Build a Complete SaaS App with Flask with a single-time purchase.