Mike Slinn
Mike Slinn

Secrets of Setting Up Django EMail

Published 2021-05-18.
Time to read: about 2 minutes.

This article is categorized under Django.

Argh, gather around and I'll tell ye a tale of the secrets of setting Up Django email. Yon scurvey dogs tend to leave out some important information in the documentation. Even those dastardly cretins answering questions at StackOverflow won’t tell the full tale, not with all the important juicy bits.

EMail Server Settings

These are the DJango settings I use to send email from one of Rackspace EMail accounts. Notice how I have highlighted a portion of the EMAIL_BACKEND value (smtp). That is the backend which will actually send email, instead of merely accepting the email request and logging it without actually sending anything email.

settings.py
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'secure.emailsrvr.com'
EMAIL_HOST_PASSWORD = '****'
EMAIL_HOST_USER = 'email@domain.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_USE_SSL = False

This is the underdocumented backend that will cause you untold grief if you don't know about it:

settings.py
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

Unfortunately, some overly helpful but horribly misguided soul decided to make the console email backend the default. That backend does not actually send email, it is just a stub.

Unit Tests Change the Middleware Without Warning

In an outstanding display of misguided overworking of an API, when in unit test mode, Django changes the middleware without notice. The documentation does mention this misbehavior, if you know where to look. You'll never find that information unless you know it is there.

The middleware that gets swapped in also does not actually send email. No, your code is not broken, you are caught between a poor design decision and inadequate documentation. I hightlighted the magic pixie dust you need to make emails work in unit tests.

thing_model/tests.py
from django.test import TestCase
from django.test.utils import override_settings

# Import EMAIL_BACKEND so the value can be used to override
# Django's misguided email middleware switcheroo for unit tests
from main.settings import EMAIL_BACKEND
from thing_model.models import ThingModel

class ThingModelTests(TestCase):

    # See https://stackoverflow.com/a/49909468/553865
    @override_settings(EMAIL_BACKEND=EMAIL_BACKEND)
    def test_result_html(self):
        thing_model.email("blah@blah.com")

HTML and/or Text EMail

Django can send plain text email, or HTML email, or both together. Here is a simplified example of how to send information from a model class as HTML and plaintext using Django email.

Model
from django.db import models
from django.core.mail import send_mail

class ThingModel(models.Model):
    # Define model fields here

    @property
    def as_html(self):
        return "<h2>Title goes here</h2>\n<p>Blah blah</p>\n"

    @property
    def as_text(self):
        return "Title goes here\nBlah blah\n"

    def email(self, email_address):
        # result is set to 1 for success, 0 for failure
        result = send_mail(
            subject = 'Test Email',
            message = self.as_text,
            html_message = self.as_html,
            from_email = 'email@domain.com',
            recipient_list = [ email_address ],
        )
        print(f"Result code for sending email={result}")