Build a Simple Mailing Service with Django
By: Juli Colombo
Tags: Python, Web Development, Django, Backend Development, Sendgrid

Build a Simple Mailing Service with Django

When creating a web application, it is frequently requested to send emails: no matter if it has to be done when users sign up in our platform, or if they forget their password, or if a payment confirmation has to be sent after a purchase. The email requirement is actually very important and it can be messy if we don’t structure an email service from the beginning.

This post aims not only to help you define a flexible email service from scratch that allows you to change the email delivery platform easily, but also to be supple in terms of the content of the actual email.

I will be using Django to show an example this time, but I hope the main idea can be spread to other frameworks and programming languages you might be using.

Let’s start!

Sending a basic email

Suppose we want to send an email to a user after he or she signs up to our web application. We can use Django’s documentation to send that email in the view after all validations were run and the user was created. We will come up with something like this:

Of course you must set up some important configurations in your settings like EMAIL_HOST and EMAIL_PORT, as the documentation says.

Great! We are already sending our welcome email!

Create a mailer

As I said before, we will probably need to send emails in several parts of our application, so it would be nice to have an email service or mailer who handles all the email requests, making it easier to add fixes or changes, because we won’t have to search through the whole code.

Let’s see how our register view will look like after this change:

Subclass mailers

Now that we have moved all the “email code” to a single place, we can take advantage of that! How about creating specific mailers that know what content to send when they are called? Let’s add a mailer that will get into action everytime new users sign up and another one that sends orders’ confirmations!

This shows that it is very easy to incorporate more mailers for different situations. You just have to let the basic mailer handle the implementation, and the subclasses set the content.

Now our register view will look much cleaner, because it doesn’t have to handle all the email stuff:

Using Sendgrid

Suppose that we have to move our email backend to Sendgrid (a customer communication platform for transactional and marketing email) using its official python library. We won’t be using Django’s send_email any longer. Instead we will have to use the new library’s syntax. Well… We are lucky! We have all the code related to email management in just one place and it will be easier for us to do this refactor 😉

Be aware that you must set Sendgrid’s api key in your settings and also note that html templates will be managed directly from Sendrid’s web page, and you will need to have the templates’ ids to specify which one should be used.

Great! It was not so difficult and we don’t have to modify each line of code where we want to send an email!

Let’s get a little bit further now.

Personalize email’s content with domain data

Of course when we send emails, it is likely to use some domain data to fill in the email’s template. For example, it would be friendlier if our welcome email had the new user’s name in it. Sendgrid allows you to define variables in the template that will be replaced with the actual information that receives from us. So let’s add this data!

The only problem I see here is that this substitutions scheme isn’t very flexible. It is likely to happen that we have to pass other data depending on the context that cannot be accessed via the user, for instance: new order’s number, reset password link, etc. There can be quite a lot of these variables and passing them as named parameters could make the code messier and dirtier. We want a keyworded, variable-length argument list, usually called **kwargs, but let’s name them **substitutions to make it more expressive:

In case you have to pass extra information to the mailer, you will do something like this:

NewOrderMailer(user.email, order_id=instance.id).send_email()

PasswordResetMailer(user.email, key=password_token.key).send_email()

Summing up

We have created a flexible mailer which encapsulates all the code related to emails in one place, making it easier to maintain, and that also receives variable context parameters to fill in the email content! Thinking of the whole approach at once could be difficult, but making it step by step is simpler and the benefits are a lot. I encourage you to add the feature of attaching files to an email using this design!

Thank you very much for reading this post and I hope it helps you for your projects. Follow me for upcoming posts, and I wish you good luck and happy coding!