Django set up mail server

Problem

You want to receive emails of events associated with a Django project and send email to end users.

Solution

Set up a default connection to an email server defining EMAIL_BACKEND and EMAIL_HOST in settings.py. If required you can also define EMAIL_PORT, EMAIL_HOST_USER, EMAIL_HOST_PASSWORD and EMAIL_USE_SSL or EMAIL_USE_TLS in settings.py. You can also set up a direct Django connection to an email service provider like Google Gmail/Google Apps, Amazon Simple Email Service (SES) and SparkPost.

Once you define a connection to an email server you can use one of Django's email shortcut methods to send emails: send_mail, send_mass_mail, mail_admins or mail_managers. To access all functionalites to send email in Django, such as adding attachments, headers, CC and BCC you can use the EmailMessage class.

How it works

There are two main aspects associated with setting up email in a Django project. The first is setting up the connection to an email server and the second is the composition of emails.

Set up a default connection to an email server

Django supports connections to any email server and also offers various options to simulate email server connections. Email simulation is particularly powerful during development and testing where sending out real emails is unnecessary. The set up for an email server in Django is done in settings.py. Depending on the email server connection, you may need to set up several variables in settings.py. Table 1 illustrates the various email server options for Django.

Table 1 - Django email server configurations
Django email backendConfigurationNotes
For development (DEBUG=True)
Console EmailEMAIL_BACKEND='django.core.mail.backends.console.EmailBackend'Sends all email output to the console where Django is running.
File EmailEMAIL_BACKEND='django.core.mail.backends.filebased.EmailBackend'
EMAIL_FILE_PATH='/tmp/django-email-dev'
Sends all email output to a flat file specified in EMAIL_FILE_PATH.
In memory EmailEMAIL_BACKEND='django.core.mail.backends.locmem.EmailBackend'Sends all email output to an in memory attribute available at django.core.mail.outbox.
Nullify EmailEMAIL_BACKEND='django.core.mail.backends.dummy.EmailBackend'Does nothing with all email output.
Python Email Server SimulatorEMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST=127.0.0.1
EMAIL_PORT=2525

also needed is the Python command line email server:

python -m smtpd -n -c DebuggingServer localhost:2525
Sends all email output to a Python email server set up via command line. This is similar to the Console Email option, because the Python email server outputs content to the console.
For production (DEBUG=False)
SMTP Email Server (Standard)EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend'
1EMAIL_HOST=127.0.0.1
1EMAIL_PORT=25
2EMAIL_HOST_USER=<smtp_user>
2EMAIL_HOST_PASSWORD=<smtp_user_pwd>
Sends all email output to a SMTP email server.
SMTP Email Server (*Secure-TLS)EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend'
1EMAIL_HOST=127.0.0.1
1EMAIL_PORT=587
2EMAIL_HOST_USER=<smtp_user>
2EMAIL_HOST_PASSWORD=<smtp_user_pwd>
EMAIL_USE_TLS=True
Sends all email output to a secure SMTP (TLS) email server.
SMTP Email Server (*Secure-SSL)EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend'
1EMAIL_HOST=127.0.0.1
1EMAIL_PORT=465
2EMAIL_HOST_USER=<smtp_user>
2EMAIL_HOST_PASSWORD=<smtp_user_pwd>
EMAIL_USE_SSL=True
Sends all email output to a secure SMTP (SSL) email server.
1If the SMTP email server is running on a network or a different port than the default, adjust EMAIL_HOST and EMAIL_PORT accordingly.
2In today's email spam infested Internet, nearly all SMTP email servers require authentication to send email. If your SMTP server doesn't require authentication you can ommit EMAIL_HOST_USER and EMAIL_HOST_PASSWORD.
*The terms SSL and TLS are often used interchangeably or in conjunction with each other (TLS/SSL). There are differences though in terms of their underlying protocol. From a Django set up prespecitive, you only need to ensure what type of secure email server you connect to, as they operate differently and on different ports.

Whichever email connection you set up from table 1 in settings.py is considered a Django project's default and will be used when doing any email related task -- if you don't specify otherwise when defining an email task.

Set up a default connection to Google Gmail/Google Apps, Amazon Simple Email Service (SES) and SparkPost

The previous section provided the most generic approach to set up a default connection to an email server in Django. However, with the complexities involved in running email servers in today's world -- namely spam filtering and security issues -- it can be easier and more practical to use a third party service to relay email from a Django project to the outside world. Although you can use the previous section's configurations to connect to any third party email service, there can be certain subtleties to set up configurations to third party email services. In this section I'll provide the Django configuration details for what I consider three of the most popular third party email services.

Note You can set up Django local email delivery and configure Exim, Postfix or Sendmail to deliver email to third party providers.

This section describes how to set up Django to connect directly with third party email providers. Another alternative is to set up Django to deliver email to a locally running email server (i.e. on 127.0.0.1) such as Exim, Postfix or Sendmail, which can then be set up to relay email to a third party email service.

I personally would not recommend this alternative as it adds another component to set up, maintain and worry about. Not to mention this is beyond the scope of Django, as it involves setting up different email servers with third party email services.

Google offers the ability to send out email through Gmail or Google Apps, the last of which is a Gmail version for custom domains (e.g. coffeehouse.com ). Once you have a Gmail or Google Apps account, you'll need to set up the account's username/password credentials in settings.py. You will not be able to use Google's email services without hard-coding your account credentials somewhere in your app. If you are weary of hard-coding the username/password credentials in settings.py, I suggest you create a separate account for this purpose to limit vulnerabilities, look into using multiple environments or configuration files for Django to keep the username/password in a different file or set up a local email server with the credentials as described in the previous sidebar.

Listing 1 illustrates the configuration needed to set up Django to send email via Gmail or Google Apps account.

Listing 1 - Django email configuration for Gmail or Google Apps account

EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST='smtp.gmail.com'
EMAIL_PORT=587
EMAIL_HOST_USER='username@gmail.com/OR/username@coffeehouse.com'
EMAIL_HOST_PASSWORD='password'
EMAIL_USE_TLS=True

As you can see in listing 1, the configuration parameters are pretty similar to those described in table 1. This is all you need to set up a default email connection to Gmail or Google Apps in Django.

Note Beware of sending too much email with Google

Because Google's email service is free, it's not designed for relaying too many email messages. If your Django app sends out a couple of email messages every hour you probably won't have a problem, but if your app sends out email messages every second or hundreds of email messages in the span of a few minutes, the account will likely be blocked.

If the account is blocked, you will either need to wait a few hours or manually log into the account (i.e. via a browser) for it to be unblocked. If the account is constantly blocked due to the email volume you send out, you should try another email service provider.

Note Google will overwrite the From: field with the account value, unless it's added as an alias.

Django allows you to set an email's From: field to any value you want. However, to avoid spoofing Google will overwrite this field to the account email (i.e.EMAIL_HOST_USER in settings.py) if the From: email value is not an alias in the Gmail or Google App account.

This means if you send an email message in Django with From: support@coffeehouse.com and this email is not set up as an alias in the Gmail or Google App account, the final email will appear with From: set to the account's main email.

SES is another email service offered by AWS which is run by Amazon.com. Unlike Google's email service, SES is a paid service with an average cost of 0.0001 cents per email (10 cents per 1000 emails). The easiest way to set up Django with SES is through the Python library boto and a custom Django email backend called django-ses.

Listing 2 illustrates the pip requirements to install boto which is a library to integrate multiple AWS services using Python and django-ses which is an open-source project specifically designed to run SES with Django. NOTE: If you are unfamiliar with pip, read the recipe Install Django which explains why pip is a good option to manage Python packages in Django projects.

Listing 2 - Python pip requirements for Amazon.com SES with Django

pip install boto
pip install django-ses

Once you install the Python packages in listing 2 using pip, you can proceed to configure SES in settings.py. Listing 3 illustrates the necessary variables to set up Django to use SES.

Listing 3 - Django email configuration for Amazon.com SES

EMAIL_BACKEND = 'django_ses.SESBackend'
AWS_ACCESS_KEY_ID = 'FZINISSZ3542DPIO32CQ'
AWS_SECRET_ACCESS_KEY = '3Nto4vknl+xeZR+1tF3L645EUyOS+zZy/uPJ1rN' 

As you can see in listing 3, the variable EMAIL_BACKEND is set to the custom class django_ses.SESBackend which provides all the necessary hooks to connect to SES.

To connect to SES you'll also need to provide the variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY which are access credentials related to your AWS account. See How Do I Get Security Credentials ? for more information on getting these values from an AWS account.

This is all you need to set up a default email connection to Amazon Simple Email Service (SES). There's no need to set up any other variable in settings.py, such a EMAIL_HOST or EMAIL_HOST_USER, everything is taken care of by the custom email backend.

SparkPost is another third party email service used by large companies like Twitter, Oracle and PayPal. Pricing wise SparkPost is a mix between the two previous services, it's a free service for the first 100,000 emails per month, but after this volume it's a paid service with an average cost of .0002 cents per email (20 cents per next 1000 emails per month) and lower per email rates once you send 1 million emails a month..

The easiest way to set up Django with SparkPost is directly in settings.py. Listing 4 illustrates the necessary variables to set up Django to use SparkPost.

Listing 4 - Django email configuration for SparkPost

EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.sparkpostmail.com'
EMAIL_PORT = 587
EMAIL_HOST_USER = 'SMTP_Injection'
EMAIL_HOST_PASSWORD = '<sparkpost_api_key>'
EMAIL_USE_TLS = True

As you can see in listing 4, the configuration parameters are pretty similar to those described in table 1 for a standard email connection. Just notice that in addition to the EMAIL_HOST_USER value being SMTP_Injection -- a SparkPost requirement -- you'll also need to assign a SparkPost API key to the EMAIL_HOST_PASSWORD. See Create API Keys for more information on how to create and get SparkPost API keys.

Now that you understand various ways to set up an email connection in a Django project, lets explore the actual composition of emails.

Built-in helpers to send email in Django

There can be many options and steps involved in sending an email. To simplify this process, Django offers four shortcut methods that you can leverage anywhere in an application (e.g. to receive an email notification when a web form is processed or when a critical error occurs). Table 2 illustrates the various email shortcut methods.

Table 2 - Django email shortcut methods
Shortcut method and descriptionShortcut method with all arguments*Argument descriptions and notes
send_mail is the most common option to send email.
send_mail(subject, message, from_email=settings.DEFAULT_FROM_EMAIL, recipient_list, fail_silently=False, auth_user=None, auth_password=None, connection=None, html_message=None)
  • subject.- Email subject string.
  • message.- Email message string.
  • from_email.- Email From: field. If not provided it's set to DEFAULT_FROM_EMAIL from settings.py which by default is webmaster@localhost.
  • recipient_list.- Email recipients as a list of strings.
  • fail_silently.- Offers the ability to bypass errors if email cannot be sent. By default set to False, which means any error when attempting to send email will raise an smtplib.SMTPException exception.
  • auth_user.- Authentication user for the SMTP server. If provided it overrides the variable EMAIL_HOST_USER in settings.py.
  • auth_password.- Authentication password for the SMTP server. If provided it overrides the variable EMAIL_HOST_PASSWORD in settings.py.
  • connection.- Django email backend to send the mail. If provided it overrides the variable EMAIL_BACKEND in settings.py. See table 1 for options.
  • html_message.- An HTML string to send an HTML & text email message. If provided, the resulting email will be a multipart/alternative email with message as the text/plain content type and html_message as the text/html content type.
send_mass_mail is more efficient than the send_mail method, because it opens a single connection to the email server and sends all messages contained in a tuple. Note though send_mass_mail does not support HTML messages unlike send_mail.send_mass_mail(datatuple, fail_silently=False, auth_user=None, auth_password=None, connection=None)
  • datatuple.- Is a tuple which contains tuples in the form (subject, message, from_email=settings.DEFAULT_FROM_EMAIL, recipient_list).
mail_admins sends email to all users defined in the ADMINS variable in settings.py.mail_admins(subject, message, fail_silently=False, connection=None, html_message=None)Email is sent with a From: field set to the variable SERVER_EMAIL in settings.py which by default is root@localhost. The email subject is prefixed with the variable EMAIL_SUBJECT_PREFIX in settings.py which by default is '[Django] '.
mail_managers sends email to all users defined in the MANAGERS variable in settings.py.mail_managers(subject, message, fail_silently=False, connection=None, html_message=None)Email is sent with a From: field set to the variable SERVER_EMAIL in settings.py which by default is root@localhost. The email subject is prefixed with the variable EMAIL_SUBJECT_PREFIX in settings.py which by default is '[Django] '.
*Method arguments without a default value (e.g. subject,message) must always be provided. Method arguments with a default value (e.g. fail_silently=False, connection=None) are optional.
Note mail_admins is automatically triggered on errors when DEBUG=False due to default logging configuration.

If you start to get emails with error messages once a project goes into production (i.e.DEBUG=False), it's because the mail_admins shortcut is automatically hooked-up for this purpose. This is due to the way Django's default logging works.

To disable this behavior you will either need to clear all values from ADMINS in settings.py or override the default logging behavior as described in the Set up logging for a Django project recipe.

Custom email - attachments, headers, CC, BCC and more - with EmailMessage.

Although the previous email shortcut methods can be used under most circumstances, they do not support things like attachments, CC, BCC or other email headers. If you want total control for sending email messages in Django the previous shortcut methods won't work.

Used 'under-the-hood' by the previous Django shortcut methods and offering the utmost flexibility for sending email in Django is the Django EmailMessage class. The various parameters and methods supported by the EmailMessage class are described in Table 3

Table 3 - Django EmailMessage class parameters and methods
Parameter and/or methodDescription
subjectThe subject line of the email.
bodyThe body text as a plain text message.
from_emailThe sender's address. Both webmaster@coffeehouse.com and Webmaster forms are legal. If omitted, the DEFAULT_FROM_EMAIL value from settings.py is used.
toA list or tuple of recipient addresses.
bccA list or tuple of addresses used as the email BCC header when sending the email.
connectionAn email backend instance. Use this parameter if you want to use the same connection for multiple messages. If omitted, a new connection is created when send() is called.
attachmentsA list of attachments to put on the message. These can be either email.MIMEBase.MIMEBase instances, or (filename, content, mimetype) triples.
headersA dictionary of extra headers to put on the message. The keys are the header name, values are the header values. It's up to the caller to ensure header names and values are in the correct format for an email message. The corresponding attribute is extra_headers.
ccA list or tuple of recipient addresses used in the the email CC header when sending the email.
send(fail_silently=False)Sends the message. If a connection was specified when the email was constructed, that connection will be used. Otherwise, an instance of the default backend will be instantiated and used. If the keyword argument fail_silently is True, exceptions raised while sending the message will be omitted. An empty list of recipients will not raise an exception.
message()Useful when extending the EmailMessage class to override and put the content you want into the MIME object. Constructs a django.core.mail.SafeMIMEText object (a subclass of Python's email.MIMEText.MIMEText class) or a django.core.mail.SafeMIMEMultipart object holding the message to be sent.
recipients()Useful when extending the EmailMessage class because the SMTP server needs to be told the full list of recipients when the message is sent. It returns a list of all the recipients of the message, whether they're recorded in the to, cc or bcc attributes.
attach()Creates a new file attachment and adds it to the message. There are two ways to call attach(). You can pass it a single argument that is an email.MIMEBase.MIMEBase instance that will be inserted directly into the resulting message or you can passs it three arguments: filename, content and mimetype (e.g.message.attach('menu.png', img_data, 'image/png'), where filename is the name of the file attachment as it will appear in the email, content is the data that will be contained inside the attachment and mimetype is the optional MIME type for the attachment. If you omit mimetype, the MIME content type will be guessed from the filename of the attachment.
attach_file()Creates a new attachment using a file from the filesystem. It can be called with the path of the file to attach (e.g.message.attach_file('/images/menu.png') and optionally with the MIME type to use for the attachment (e.g.message.attach_file('/images/menu.png','image/png'). If the MIME type is omitted, it will be guessed from the filename.

With a clear idea of the functionalities provided by the EmailMessage classs in table 3, lets take a look at some typical cases where you would use the EmailMessage class to send email.

Listing 5 provides a basic email example that uses options like CC, BCC and the Reply-To header which aren't support via the Django email shortcuts from the last section.

Listing 5 - Send basic email with EmailMessage class

from django.core.mail.message import EmailMessage

# Build message
email = EmailMessage(subject='Coffeehouse specials', body='We would like to let you know about this week\'s specials....', from_email='stores@coffeehouse.com',
            to=['ilovecoffee@hotmail.com', 'officemgr@startups.com'], bcc=['marketing@coffeehouse.com'], cc=['ceo@coffeehouse.com']
            headers = {'Reply-To': 'support@coffeehouse.com'})

# Send message with built-in send() method
email.send()

As you can see in listing 5, we create an EmailMessage instance specifying the different class parameters. Once this is done, we simply call the send() method to send the email. It's as simple as that. Because no connection values are provided in the EmailMessage instance, Django uses the default backend connection defined in settings.py.

One drawback of the EmailMessage send() method is that it opens a connection to the email server every time it's called. This can be inefficient if you send hundreds or thousands of emails at once. In the spirit of the send_mass_mail() shortcut method from the last section, it's also possible to open a single connection to the email server and send multiple emails with EmailMessage. Listing 6 shows how to use a single connection and send multiple emails with EmailMessage.

Listing 6 - Send multiple emails in a single connection with EmailMessage class

from django.core import mail
connection = mail.get_connection()

# Manually open the connection
connection.open()

# Build message
email = EmailMessage(subject='Coffeehouse specials', body='We would like to let you know about this week\'s specials....', from_email='stores@coffeehouse.com',
            to=['ilovecoffee@hotmail.com', 'officemgr@startups.com'], bcc=['marketing@coffeehouse.com'], cc=['ceo@coffeehouse.com']
            headers = {'Reply-To': 'support@coffeehouse.com'})
# Build message
email2 = EmailMessage(subject='Coffeehouse coupons', body='New coupons for our best customers....', from_email='stores@coffeehouse.com',
            to=['officemgr@startups.com','food@momandpopshop.com'], bcc=['marketing@coffeehouse.com'], cc=['ceo@coffeehouse.com']
            headers = {'Reply-To': 'support@coffeehouse.com'})

# Send the two emails in a single call
connection.send_messages([email, email2])
# The connection was already open so send_messages() doesn't close it.
# We need to manually close the connection.
connection.close()

In listing 6 we first create a connection to the email server using mail.get_connection() and then open the connection with the open() method. Next, we create the EmailMessage instances. Once the email instances are prepared, we call the connection's send_messages() method with an argument list corresponding to each of the EmailMessage instances. Finally, once the emails are sent we call the connection's close() method to drop the connection to the email server.

Another common email scenario is to send HTML emails. Django provides the EmailMultiAlternatives class for this purpose which is a subclass of the EmailMessage class. By being a subclass, it means you can leverage the same functionalities as EmailMessage (e.g. CC, BCC), but you don't need to do a lot of work as the subclass EmailMultiAlternatives is specifically designed to handle a multiple types of messages. Listing 7 illustrates how to use the EmailMultiAlternatives class.

Listing 7 - Send HTML (w/text) emails with EmailMultiAlternatives, a subclass of the EmailMessage class.

from django.core.mail import EmailMultiAlternatives

subject, from_email, to = 'Important support message', 'support@coffeehouse.com', 'ceo@coffeehouse.com'
text_content = 'This is an important message.'
html_content = '

This is an important message.

' msg = EmailMultiAlternatives(subject=subject, body=text_content, from_email=from_email, to=[to]) msg.attach_alternative(html_content, "text/html") msg.send()

In listing 7 we first define all the email fields, which include the text and HTML version of the email. Note that having a text and HTML version of the email content is common practice as there is no guarantee end users will allow or can read an email's HTML version, so a text version is provided as a backup. Next, we define an instance of the EmailMultiAlternatives class, notice the parameters are inline with those of the EmailMessage class.

Next, in listing 7 you can see we call the attach_alternative method which is specific to the EmailMultiAlternatives class. The first argument to this method is the HTML content and the second is the content type that corresponds to text/html. Finally, we call the send() method -- part of the EmailMessage class, but which is also automatically part of to EmailMultiAlternatives since it's a subclass -- to send the actual email.

In controlled environments (e.g. corporate email) where it can be guaranteed that all end users are capable of viewing HTML email, it can be practical to just send an HTML version of an email and bypass the text version altogether. Under these circumstances, you can actually use the EmailMesssage class directly with a minor tweak. Listing 8 illustrates how to just send HTML email with the EmailMessage class.

Listing 8 - Send HTML emails with EmailMessage class

subject, from_email, to = 'Important support message', 'support@coffeehouse.com', 'ceo@coffeehouse.com'
html_content = '

This is an important message.

' msg = EmailMessage(subject=subject, body=html_content, from_email=from_email, to=[to]) msg.content_subtype = "html" # Main content is now text/html msg.send()

Listing 8 looks like a standard EmailMessage process definition, however line four is what makes listing 8 different. If the HTML content were sent without line four, end users would receive a verbatim version of the HTML content (i.e. without the HTML brackets rendered). This is because by default the EmailMessage class specifies the content type as text. In order to switch the default content type of the EmailMessage instance, in line four a call is made to set the content_subtype to html. With this change the email content type is set to HTML and end users are capable of viewing the content rendered as HTML.

Note Beware of just sending HTML email versions to the public at large

Although sending an HTML email version is quicker than sending a text and HTML email version, this can be problematic if you can't determine where end users read their email. There are certain users that for security reasons disable the ability to view HTML emails, as well as certain email products that can't or aren't very good at rendering HTML emails. So if you just send an HTML email version, there can be a subset of end users that won't be able to see the email content.

For this reason if you send email to end users where you can't control their environment (i.e. email reader), it is best you send a text and HTML email version -- as illustrated in listing 7 -- than sending an HTML email version illustrated in listing 8.

Another common practice when sending emails is to attach files. Listing 9 illustrates how to attach a PDF to an email.

Listing 9 - Send email with PDF attachment with EmailMessage class

from django.core.mail.message import EmailMessage

# Build message
email = EmailMessage(subject='Coffeehouse sales report', body='Attached is sales report....', from_email='stores@coffeehouse.com',
            to=['ceo@coffeehouse.com', 'marketing@coffeehouse.com']
            headers = {'Reply-To': 'sales@coffeehouse.com'})
# Open PDF file
attachment = open('SalesReport.pdf', 'rb')
# Attach PDF file
email.attach('SalesReport.pdf',attachment.read(),'application/pdf')

# Send message with built-in send() method
email.send()

As you can see in listing 9, after creating an EmailMessage instance we just open the PDF file using Python's standard open() method. Next, we use the attach() method from the EmailMessage which takes three arguments: the file name, the file contents and the file content type or MIME type. Finally, we simply call the send() method to send the email.