Fork me on GitHub

Debugging Emails in Django

In many applications you need some way to debug / test the sending of emails without actually sending them out. For instance, you may be working on a political action based application and part of the functionality of the app is to automatically email your congressional representatives about a particular issue. While testing an application like this you want to be able to verify that the appropriate emails are setup to be delivered, but you don't want them to actually get delivered. This sort of testing does not replace any integration level tests you would write, but it serves a different purpose. Often, client signoff on an application requires end-user testing. These debug emails could be used to verify the application is working correctly to obtain a signoff.

Wrapping Django's send_mail

In every Django project I have an app called core. Core is something I've built up over time to include common things I want in every project. Core contains things like admin screens for LogEntry, ContentType, and others. It also includes a wrapped version of Django's send_mail that I use whenever I need to send email messages.

from django.conf import settings
from django.core.mail import send_mail as send_mail_original

def send_mail(subject, message, from_email, recipient_list,
              fail_silently=False, auth_user=None, auth_password=None,
              connection=None):
    if isinstance(recipient_list, basestring):
        recipient_list = [recipient_list]

    if getattr(settings, 'EMAIL_DEBUG_ON', True):
        # wrap the email message
        wrapper = "-------------------------------------------------\n" \
                  "- DEBUG EMAIL                                   -\n" \
                  "-------------------------------------------------\n" \
                  "- To: %(to)s                                    -\n" \
                  "- From: %(from)s                                -\n" \
                  "- Subject: %(subject)s                          -\n" \
                  "-------------------------------------------------\n\n" \
                  % {
                        'to': ", ".join(recipient_list),
                        'from': from_email,
                        'subject': subject
                    }

        message = wrapper + message
        subject = "DEBUG: %s" % subject
        recipient_list = getattr(settings, 'DEBUG_EMAILS',
                                (k for k,v in settings.ADMINS.items()))

    send_mail_original(subject, message, from_email, recipient_list,
                       fail_silently=fail_silently, auth_user=auth_user,
                       auth_password=auth_password, connection=connection)

The code is pretty self explanatory. This simple method allows you to send email messages, but wrap them so they are actually only delivered to the ADMINS, or DEBUG_EMAILS if you have specified that setting. DEBUG_EMAILS is a list of email addresses to send to.

Using django-mailer-2

Another method that can be used to verify the contents of sent emails is to use the django-mailer-2 application. I prefer this one by SmileyChris over the original by James Tauber, but both fundamentally do the same thing. django-mailer-2 captures, queues, and sends out emails via a cron job, in place of the standard "fire and forget" method. django-mailer-2 provides a method to monkey-patch the Django version of send_mail so that its specialized version is used instead.

This simple addition to my urls.py module means I don't have to modify any of my application code.

# queue all email
from django_mailer import queue_django_mail
queue_django_mail()

Once you have django-mailer-2 wired into your project any emails will automatically be queued up into the django-mailer-2 tables. As long as the django-mailer-2 cron job entries are not running on your test server, the messages will never be sent out. Incidentally, I have my own forked version of django-mailer-2 that only makes modifications to the admin in order to make it simpler to find mail messages.

This application is also very useful when working in a development environment, especially if you are not running a mail server on your local development system.

Hopefully these couple of tips will help you test and debug Django emails more simply in the future.

You should follow me on Twitter.

blogroll

social