A Django application I’ve been working on sends a confirmation email near the end of a workflow. The content of that email closely matches the final web page of the workflow. Constructing an email in Django is pretty straightforward, and using templates makes the process a lot easier and more flexible.
My naive initial approach was to construct my HTML in a string, using Python’s string formatting, and then use the string as the body of the email. That was a reasonable starting point, but it sucked because it’s messy, it duplicates functionality and frankly it’s a waste of the power Django has to offer.
The next step was to harness templates to generate my content for the email. I actually need an email with both HTML and plain text content, and I already mentioned that the content is reasonably close to that used elsewhere, so there’s plenty of potential for code reuse.
The django.template.loader module contains the handy render_to_string function which, surprise, allows us to render our template to a string. It parallels the more common render_to_response function found in your view code, so I can supply a template name and a context in the same way I would for my view code: (contrived example follows)
from django.template.loader import render_to_response html_content = render_to_string( "my_template.html", my_context )
I’ll go into that HTML content shortly, but suffice to say the template can reuse snippets of template code from the rest of my application. I can therefore create reusable content that is embeddable in both my web page and email body. Likewise, I can generate a text version of the body, though reusing HTML snippets is obviously not so easy or even appropriate:
text_content = render_to_string( "my_template.txt", my_context )
I can then take these strings and use them to construct my email:
from django.core.email import EmailMultiAlternatives my_mail = EmailMultiAlternatives( subject="Foo Confirmed", body=text_content, from_email="email@example.com", to=[ "firstname.lastname@example.org" ] ) my_mail.attach_alternative( html_content, "text/html" )
Now, back to the HTML content. There are a few things to be aware of with HTML in email:
- Keep the <head></head> section empty as the content is likely to be ignored and could even be stripped out
- Use a <style></style> section after your <body> tag to inline your CSS code
- Inline references to external resources like images are likely to be blocked
Think of the HTML in your email body as needing to be a self-contained environment. I’ve included some online resources at the end of this post which will give you more in-depth information.
That’s fine for CSS, but how do you add inline images to your emails? The answer is to create MIMEImage instances of any images and attach to your email with appropriate Content-ID and Content-Disposition headers. There’s a very simple example at http://www.djangosnippets.org/snippets/1507/ that worked nicely for me.
Hope the above is a useful starting point!