Introduction
Pretty much every user-based app and website needs to send an email to the user at some point, so eventually you'll have to deal with the joyous world of programmatic emailing. There are quite a few services popping up to help you with this, but with every app having its own unique requirements, few of these services adequately get the job done.
So having faced the problem a few times (and in a few languages), I decided to write up this short post on how to send email with boto and SES. The boto library is a very nice wrapper for Python that helps you interact with the AWS API. To see some more of its capabilities, check out my other boto article on how to upload a file to s3.
Setup
Before you can really do anything, you'll need an AWS account (obviously) along with the access key and secret key to your account, which will be used for authenticating with the SES servers. There are a few different ways you can use the keys to authenticate, but here we'll just be passing them to the connect_to_region
method provided by boto.
Next, you'll need to either verify with SES an email address (a Gmail address works just fine) or a domain you own. If you're just testing this feature out, I'd suggest verifying only a single email address since it's a bit quicker. All you need to do is click on a link in the verification email they send you, as opposed to adding TXT records to your zone file for verifying a domain.
If you're just using SES for the first time and your application will need to send out a high volume of emails, then you may need to submit a request to increase your Sending Quota. Your SES account is initially put into a sandbox, which means you can only send 200 emails within a 24 hour period. Once you've successfully requested to increase your sending limits then you should be able to send many more (I was approved for 50,000 per day, which is far more than I need).
The Code
Once you've done the initial setup I mentioned above, you should be able to use the code below to send emails.
import boto.ses
AWS_ACCESS_KEY = 'YOUR-ACCESS-KEY-HERE'
AWS_SECRET_KEY = 'YOUR-SECRET-KEY-HERE'
class Email(object):
def __init__(self, to, subject):
self.to = to
self.subject = subject
self._html = None
self._text = None
self._format = 'html'
def html(self, html):
self._html = html
def text(self, text):
self._text = text
def send(self, from_addr=None):
body = self._html
if isinstance(self.to, basestring):
self.to = [self.to]
if not from_addr:
from_addr = '[email protected]'
if not self._html and not self._text:
raise Exception('You must provide a text or html body.')
if not self._html:
self._format = 'text'
body = self._text
connection = boto.ses.connect_to_region(
'us-east-1',
aws_access_key_id=AWS_ACCESS_KEY,
aws_secret_access_key=AWS_SECRET_KEY
)
return connection.send_email(
from_addr,
self.subject,
None,
self.to,
format=self._format,
text_body=self._text,
html_body=self._html
)
To use this code, all you need to do is this:
email = Email(to='[email protected]', subject='You are HTML Awesome')
email.text('This is a text body. Foo bar.')
email.html('<html><body>This is a text body. <strong>Foo bar.</strong></body></html>') # Optional
email.send()
The email.html()
call is optional. If you include both text and HTML in your email then both will be included in the resulting MIME and the email client will show whichever format is supported or preferred by the user.
Using Email Templates
If you want to get really fancy, try using a template engine. Instead of passing the email body string directly, we can load it from a template, much like you would render an HTML page in a web framework like Django
.
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
Here we use the Jinja2 templating engine to handle the template loading and rendering:
import boto.ses
from jinja2 import Environment, PackageLoader
# Loads templates from the yourapp.templates folder
env = Environment(loader=PackageLoader('yourapp', 'templates'))
AWS_ACCESS_KEY = 'YOUR-ACCESS-KEY-HERE'
AWS_SECRET_KEY = 'YOUR-SECRET-KEY-HERE'
class Email(object):
def __init__(self, to, subject):
self.to = to
self.subject = subject
self._html = None
self._text = None
def _render(self, filename, context):
template = env.get_template(filename)
return template.render(context)
def html(self, filename, context):
self._html = self._render(filename, context)
def text(self, filename, context):
self._text = self._render(filename, context)
def send(self, from_addr=None):
# Same as before...
Note: For production code, don't actually put your AWS security keys directly in your code. Use something like environmental variables instead.
Using this code is similar to before, but instead we'll just pass the template filename and the context from which the template will be populated:
email = Email(to='[email protected]', subject='OMG an Email!')
ctx = {'username': user.username}
email.text('email.txt', ctx)
email.html('email.html', ctx) # Optional
email.send()
Now you can easily create and render HTML emails just like you do webpages.
Conclusion
Hopefully this short tutorial was useful to you. The code here should be good for most use-cases, although you can get even more advanced by adding CC, Bcc, reply-to addresses, return paths, or even file attachments.
All of these extra features I just mentioned, except attachments, can be handled by the send_email
function. To send attachments you'll have to use the lower-level send_raw_email
function, which requires you to construct the MIME message yourself. Don't worry, that isn't too hard thanks to Python's email package, but we'll save that for another article.
Was this useful to you? Did you find a mistake? Let me know in the comments!