Sending HTML Email in Django with Anymail
Tags: Coding, Django, Backend Development, Backend, Programming

Animal fetches Anymail!

What is Anymail?

Anymail is a backend package, developed for Django, which provides integration from Django to some transactional Email Service Providers (ESPs). Anymail integrates ESPs, such as Amazon SES, Mailgun, Mailjet, Postmark, and SendinBlue with Django’s builtin django.core.mail package. Among all ESPs supported by Anymail, let’s take a look at Mailgun. Mailgun is an API, built for developers who need a service to send, receive, and track emails effortlessly. Setting up Mailgun is not that difficult, therefore for this article, we’ll try to use Mailgun as our ESP.

Understood! But then, why do we need this?

When developing web-based application, make wonderful frontend and effective backend are important. However, one thing that is equally important with them is the way we care about our users. The way we express our gratitude to them, the way we show them that they are very important for us to grow. One of many possible ways to do that is by giving feedback to them using email, whenever we need to inform something to them or even just let them know about their status in our application. For example, we need to send email to our users to let them verify their account when they register to our application for the first time.

We also need to email them to let them know if their registration process is success or not, and if it is a success, we can tell them more about our app’s features and services in the same email. Even when our users forget their password, we can email them a link to recover their account and create a new password. Moreover, we can use email to update our users with our promotions and events information, so that they won’t miss anything from our app. Not only making our users comfortable, sending email can be one effective way to market and promote our app over and over again.

Wow, that’s quite interesting. Should we start setting up?

Why not? First of all, we need to sign up to Mailgun and register our domain to get Mailgun access key. You can register without credit card for restricted account (you can only send to authorised recipients), therefore if you want to have unrestricted access to all recipients, you need to register your credit card. After signing up, you need to register your domain and wait for them to verify your domain before you can get your access key. If you need detailed tutorial to get Mailgun’s access key, you can go to this link.

Anyway, let’s start setting up anymail in our Django project. In this article, we assume you’ve already had a Django project with Django applications inside it. Don’t forget to install django-anymall with Mailgun package using this command:

pip install django-anymail[mailgun]

Open your project’s settings.py, add “anymail” to INSTALLED_APPS, and add these line to your file:

ANYMAIL = { "MAILGUN_API_KEY": "<your Mailgun access key>", "MAILGUN_SENDER_DOMAIN": "<your Mailgun domain>", } EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend" DEFAULT_FROM_EMAIL = "<your email address>"

You can also use os and django.environ to save your Mailgun access key, Mailgun domain, and default email address in .env file and get them using os.getenv function.

Set up check. Now, how can I send verification email?

If you are using django.allauth as your user authentication backend, it will automatically send verification email to your user after they signed up. However, the default email template is only using text, so it’s not that attractive. You can override this default mail template with your own HTML template. Using HTML template, you can experiment with layouts, colors, images, and try different combinations with them to make attractive email template.

To override verification email template, create templates folder in your project. Inside that folder, create account folder with email folder inside it. Inside this email folder, create 4 files:

email_confirmation_message.html

Inside this file, add your custom HTML email template. You can make it manually or using online tools like this site.

email_confirmation_signup_message.html

We don’t need to much change in this file, just add this line:

{% include “account/email/email_confirmation_message.html” %}

email_confirmation_signup_subject.txt

Just like the signup-message one, for this file, we only need to add this line:

{% include “account/email/email_confirmation_subject.txt” %}

email_confirmation_subject.txt

This file will override your email subject. That’s why, we need to add these lines inside it:

{% load i18n %} {% autoescape off %} {% blocktrans %} <your verification email subject> {% endblocktrans %} {% endautoescape %}

If you are using rest-auth, you can also override the password-reset email by making registration folder inside templates folder, and adding these files:

html_password_reset_email.html

Inside this file, add your custom HTML email template. You can make it manually or using online tools like this site.

password_reset_subject.txt

This file will override your email subject. That’s why, we need to add these lines inside it:

{% load i18n %} {% autoescape off %} {% blocktrans %} <your password reset email subject> {% endblocktrans %} {% endautoescape %}

An extra tip, if you want to add images in your HTML email template, you need to upload your images to online url first and then use that url as your image source in your template.

How about sending welcome email after user’s verified?

For doing this, we need to make our own Django application which override django.rest-auth. Inside this application, create a structure which is similar to the real django.rest-auth, especially urls.py and views.py. For every class in views.py that you don’t want to change, just write a class that extends the original function and write pass.

If you want to send welcome email after a user is verified, you need to override VerifyEmailView inside your app, especially inside registration folder’s views.py, by making CustomVerifyEmailView (extends the original VerifyEmailView). Inside this class, you can override the POST method. This is the original VerifyEmailView POST method:

def post(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) self.kwargs[‘key’] = serializer.validated_data[‘key’] confirmation = self.get_object() confirmation.confirm(self.request) return Response({‘detail’: _(‘ok’)}, status=status.HTTP_200_OK)

Welcome email should be sent after a user verify his/her email address, so the verified status should be changed from False to True. How can we check this? We can get the unchanged value of verified status from confirmation object before it calls confirm function and save it to variable, just using this code:

unchanged = confirmation.email_address.verified

To get the after-change status, we can save the second status after confirm function is called to another variable:

changed = confirmation.email_address.verified

If unchanged is False and changed is True, we can send our HTML email. Let’s say we have our template in templates/email/signup.html which needs username variable in it. We can get username of our user using his/her email:

from django.contrib.auth import get_user_model UserModel = get_user_model() email = confirmation.email_address.email username = UserModel.objects.filter(email=email)[0].username

Our code to send the email should look like:

if (not(before) and after): title = “You’re verified! Welcome to our family!” html_message = loader.render_to_string(“email/signup.html”, {“username”: username},) receiver = email send_mail( title, strip_tags(html_message), settings.DEFAULT_FROM_EMAIL, [receiver], fail_silently=False, html_message=html_message, )

Don’t forget to import all needed packages and libraries:

from django.utils.html import strip_tags from django.core.mail import send_mail from django.conf import settings from django.template import loader from rest_framework.response import Response from django.utils.translation import ugettext_lazy as _ from rest_framework import status

Done! Quite simple, isn’t it?

Yeah, it’s quite simple, because basically Django has django.core.mail built in and Anymail has provided integration between Django and Mailgun. Using Mailgun, not only sending and receiving emails, we can also track and manage our email transactions. In order to send emails for special purposes like welcoming users, we only have to know which templates and classes we should override.

References

Mailgun

Rest-auth

Allauth

Django

Anymail