Tue 13 July 2010

Rendering emails with Django templates

I talk a lot about Javascript on this blog: client-side, server-side, wherever the language can be used. It's not the only language I enjoy working in, though; Python has always had a bit of a soft spot with me, and with that comes some inevitable Django love.

I use Django for a lot of my server side tasks where I don't feel I can safely use something like Node.js. On top of being battle tested beyond belief, it's a veritable bag of magic tricks just waiting to be used. Take this one case I ran into recently: I'm building a service (stealth at the moment, stay tuned) that has to do something as simple as send out an email when a new user signs up.

Now, that email should ideally contain a few things. Let's assume that, for a basic example, we're gonna just include their username, a brand new password, and their full name (how we'll use each of these is shown in depth below). Django makes sending mail pretty easy, using the send_mail function:

# Make sure you import this, otherwise... well, you get the idea.
from django.core.mail import send_mail

# send_mail is pretty straightforward, as you can see.
send_mail('Title', "Body", "from_addy", ["emails", "sent", "to"], fail_silently = True)

That's all well and good, but what if we want to do more than just a simple string for our body message? Ideally, we should be able to treat this like any other Django template. In practice, this is actually incredibly easy (and fun).

The code should be fairly well documented, but for those who'd like a little more verbose of a walkthrough, it's pretty simple: instead of passing in a string, load up a template and pass it a rendering context. See, what got me the first time around is that the render method of get_template needs a Template Context to do the dirty work, not a standard dict.

# We should all know what this is used for by now.
from django.core.mail import send_mail

# get_template is what we need for loading up the template for parsing.
from django.template.loader import get_template

# Templates in Django need a "Context" to parse with, so we'll borrow this.
# "Context"'s are really nothing more than a generic dict wrapped up in a
# neat little function call.
from django.template import Context

# Some generic stuff to parse with
username = "fry"
password = "password_yo"
full_name = "Philip J Fry"

# Our send_mail call revisited. This time, instead of passing
# a string for the body, we load up a template with get_template()
# and render it with a Context of the variables we want to make available
# to that template.
send_mail(
    'Thanks for signing up!',
    get_template('templates_path/email.html').render(
        Context({
            'username': username,
            'password': password,
            'full_name': full_name
        })
    ),
    '[email protected]',
    ['[email protected]'],
    fail_silently = True
)

With that, here's an example of an email template - it's literally just a Django template, but ready for all your email attributes.

Welcome {{ full_name }}!

Your username is {{ username }} and your password is {{ password }}.

- Management 

Django's templating system is incredibly flexible, and can easily be used for more than just your generic views. If you have any questions or suggestions, feel free to throw them in the comments!

Ryan around the Web