A primer on sending email thru Django

Hernán Tylim
Django Musings
Published in
4 min readApr 15, 2016

This is not an in-depth article on how to send emails but rather a starting point. But if you are building a website in Django and you need such website to start sending, let’s say, marketing emails today, this article is for you.

Before we start. If instead of reading me you want to go the source, this is it: https://docs.djangoproject.com/en/1.9/topics/email/

SMTP Configuration

To use Django’s own wrappers you’ll need to set up your SMTP configuration in the settings file. These are the relevant settings:

  • EMAIL_HOST
  • EMAIL_PORT
  • EMAIL_HOST_USER
  • EMAIL_HOST_PASSWORD
  • EMAIL_USE_TLS
  • EMAIL_USE_SSL

They are self-explanatory.

Quick’n dirty mail sending

from django.core.mail import send_mail

send_mail('Subject here', 'here is the message', 'from@example.com', ['to@example.com'], fail_silently=False)

That’s it.

This is useful if you want to send emails to you and you don’t care on formatting, making them multi-part, adding attachments, etc.

send_mail() docs here.

If you want to send a bunch of mails at the same time (and avoid creating a new connection to the SMTP server for each mail) you can use send_mass_mail(). Docs here.

Other shortcuts

Django also provides other shortcuts to send quick emails to site admin users.

That is, as in the ADMIN setting (link).

The wrapper is mail_admin() docs here.

And as with ADMIN and mail_admin(), you have the setting MANAGERS and mail_managers(). Docs here.

What I used

For my own use I needed to send proper emails (I was sending marketing emails). Which needed to be multi-part. (a nice looking HTML email + a properly formatted even though plain text version of the same email), and for that I needed to use the EmailMessage class. Docs here.

Example:

from django.core.mail import EmailMessage

email = EmailMessage('Hello', 'Body goes here', 'from@example.com',
['to1@example.com', 'to2@example.com'],
['bcc@example.com'],
reply_to=['another@example.com'],
headers={'Message-ID': 'foo'})
email.send(fail_silently=False)

And if you want to create an attach you just do:

email.attach('design.png', img_data, 'image/png')

Or:

email.attach_file('/images/weather_map.png')

And will pick up the attachment from the file-system.

Multi-Part Messages

But what happens if you want to send a multi-part message. You do it this way:

from django.core.mail import EmailMultiAlternatives

subject = 'hello'
from_email = 'from@example.com'
to = 'to@example.com'
text_content = 'This is an important message.'
html_content = '<p>An <strong>important</strong> message.</p>'
msg = EmailMultiAlternatives(subject,
text_content, from_email, [to])
msg.attach_alternative(html_content, "text/html")
msg.send(fail_silently=False)

That is good. But if we are going to do HTML messages, wouldn’t it better to use Django’s templates to make them?

Using Django’s Templating System for our emails

from django.template import Context
from django.template.loader import get_template
from django.core.mail import EmailMultiAlternatives
html_template = get_template('app/email_template.html')
text_template = get_template('app/email_template.txt')
context = Context({'FNAME':'John',
'LNAME':'Smith'}
html_alternative = html_template.render(context)
text_alternative = text_template.render(context)
msg = EmailMultiAlternatives(subject, text_alternative,
from_email, [to_email])
msg.attach_alternative(html_alternative, "text/html")
msg.send(fail_silently=False)

In the example above we have 2 templates ‘app/email_template.html’, and ‘app/email_template.txt’. These 2 templates are regular templates, like the ones that we use for rendering Views and we have them in the same template subdirectory (if not then the get_template() call wont find them)

Once we have both Template instances we call render() to pass along a Context object and that way generate the content that we want.

The remaining is just sending the email.

But that is not all.

Making HTML Emails are more art than science

Or so they say. The thing is that each email client renders the HTML content a little different. So if you want your email to look exactly the same in Outlook desktop, as in Outlook web, as in Gmail, as in Thunderbird, as in any other program you better use an HTML template as starting point.

You should google for “Email Templates” there are many. The one that I chose was this one. It was simple, it was responsive, it was more than I needed.

But. Once you get the email there is a thing with the CSS. Not all clients supports CSS defined in the standard way (you know with in the <style/>), they need to have the CSS inlined in each element.

Instead of ruining your neat html I suggest you use a CSS Inliner tool. You can use this one, free, from Mailchimp: Here.

Just don’t forget that the HTML that you need to render with your Template and later include in the EmailMultiAlternatives() message is the inlined one.

Bonus tip

If you don’t want to write the plain-text version of your emails (you have the HTML already in place after all), you can use this tool to make an acceptable plain-text version of an HTML email, also from Mailchimp. Here.

What about debugging and all that jazz?

By default Django already has the correct EMAIL_BACKEND configured for you to start sending emails right away.

But you can change the default EMAIL_BACKEND from the settings file. And Django provides several that you can use for debugging.

The one that is used by default and I’ve been using is this one:

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'

For testing you can use a “Memory Backend”:

EMAIL_BACKEND = ‘django.core.mail.backends.locmem.EmailBackend’

Now this one is used also by default when you are doing unit testing (python manage.py tests)

With this backend no email is sent, but they are appended to a list in the variable outbox under django.core.mail

So you can write unit tests that inspect that list and check for your EmailMessage() instances

Doc here.

Other one for testing is a “Console Backend”:

EMAIL_BACKEND = ‘django.core.mail.backends.console.EmailBackend’

This one will just output the email messages thru the console.

Another one for testing is a “File Backend”:

EMAIL_BACKEND = ‘django.core.mail.backends.filebased.EmailBackend’
EMAIL_FILE_PATH = ‘/tmp/app-messages’

This one will dump each email in the specified path

Last but not least is the “Dummy Backend”:

EMAIL_BACKEND = ‘django.core.mail.backends.dummy.EmailBackend’

Which does just nothing.

--

--