Simple Django Tip #7

Send email using MailerSend

ยท

8 min read

Introduction

Sending emails is one of the most common task in any web application. Be it while signing up, password reset, notifications, payment related, and much more - sending email has become a mandatory aspect. In the past in my Django applications, I used Gmail to send emails.

For the SaaS product I'm building I decided to use a comprehensive tool to send transaction emails, as there is quite a lot of email sending functionality built as part of the product. On exploring tools like Brevo, Mailchimp, Hubspot, Zoho, Convertkit, I finally decided to go ahead with MailerSend.

Sharing the steps that need to be configured so that it'll be helpful for anyone setting up transactional emails in your Django application.

Install Django & Create a project

Let's perform the usual steps of creating a virtual environment, followed by installing Django, and creating a project.

py -m venv venv
venv\Scripts\activate
pip install Django
django-admin startproject djangoandmailersend
cd djangoandmailersend

Start the development server to ensure the initial steps bring up the default page.

python manage.py runserver

Boom ๐Ÿ’ฅ

Build a simple flow

What's the fun without a use case, isn't it? Let's add a plain html that has an input box for the user to enter an email address and click on a button "Surprise me!". The user should receive an email with an inspirational quote.

Let's first write the required skeleton code except for the email sending and fetching a quote.

  1. Add a folder called templates under djangoandmailersend folder.

  2. In the newly created folder add a file and name it home.html

  3. Navigate to settings.py and add the path to fetch the templates against DIRS property like ๐Ÿ‘‡

     import os
     ...
     ...
     TEMPLATES = [
         {
             "BACKEND": "django.template.backends.django.DjangoTemplates",
             "DIRS": [os.path.join(BASE_DIR, "djangoandmailersend", "templates")],
             "APP_DIRS": True,
             "OPTIONS": {
                 "context_processors": [
                     "django.template.context_processors.debug",
                     "django.template.context_processors.request",
                     "django.contrib.auth.context_processors.auth",
                     "django.contrib.messages.context_processors.messages",
                 ],
             },
         },
     ]
    
    1. Create a file views.py at the same level as urls.py.

    2. Add the following method to the newly added views.py

    3.  def send_motivational_quote(request):
           return render(request, "home.html", 
                 {"message": "Check your email to feel inspired"})
      
    4. Add the following to home.html

       {% load static %}
      
       <!DOCTYPE html>
       <html lang="en">
       <head>
           <meta charset="UTF-8">
           <meta name="viewport" content="width=device-width, initial-scale=1.0">
           <title>Django and MailerSend</title>
           <style>
               body {
                   display: flex;
                   flex-direction: column;
                   justify-content: center;
                   align-items: center;
                   min-height: 100vh;
                   margin: 0;
               }
               .form-container {
                   text-align: center;
               }
               .text-input {
                   padding: 10px;
                   width: 300px;
                   font-size: 16px;
                   margin-bottom: 20px;
                   border: 1px solid #ccc;
                   border-radius: 5px;
               }
               .submit-button {
                   padding: 10px 20px;
                   font-size: 18px;
                   background-color: #fb8500;
                   color: #fff;
                   border: none;
                   border-radius: 5px;
                   cursor: pointer;
               }
               .message {
                   font-size: 16px;
                   margin-top: 20px;
                   color: #fb8500; 
               }
           </style>
       </head>
       <body>
           <div class="form-container">
               <h2>I'd like to receive a motivational quote!</h2>
               <form action="{% url 'send-email' %}" method="post">
                   {% csrf_token %}
                   <input type="text" class="text-input" name="textbox_name" placeholder="Enter email here">
                   <br>
                   <button type="submit" class="submit-button">Surprise me!</button>
                   <br>
                   <p class="message">{{ message }}</p>
               </form>
           </div>
       </body>
       </html>
      
      1. ๐Ÿ’ก
        In order to keep things simple, I added views.py in the main project itself. Likewise, I added CSS styles in the HTML file itself. In a real environment, this is not advised.
    5. On refreshing the page now, this is how it should look like:

      Cool, isn't it? ๐Ÿ˜Ž

Implement the Motivational quote logic

Let's use the free API from API Ninjas. As a first step, sign up with API Ninjas.

On successful signup, login to your API Ninjas account. On the dashboard page, you should see your API Key. Copy the same.

Let's come back to our workspace. In order to store the API Key, it is good practice to store them in a .env file and access it using the environ's get method.

Create a file under djangoandmailersend folder and name it .env. Add a property as below:

API_NINJAS_API_KEY=<YOUR-API-KEY-HERE>

We'll write a method that invokes an API from API Ninjas that will return a JSON object with a quote, author and the category of the quote.

from dotenv import load_dotenv
import os
import requests

load_dotenv()

def fetch_motivational_quote():
    """ Invokes API Ninja's API to fetch an inspirational quote
    """
    category = 'inspirational'
    api_url = 'https://api.api-ninjas.com/v1/quotes?category={}'.format(category)
    API_KEY = os.environ.get('API_NINJAS_API_KEY')
    response = requests.get(api_url, headers={'X-Api-Key': API_KEY})
    if response.status_code == requests.codes.ok:
        print(response.text)
    else:
        print("Error:", response.status_code, response.text)


def send_motivational_quote(request):
    fetch_motivational_quote()
    return render(request, "home.html", \
                  {"message" : "Check your email to feel inspired!"})
๐Ÿ’ก

Now that we have completed a standalone method to fetch an inspirational quote, let's now focus on setting up and configuring MailerSend.

Sign up with MailerSend as the first step. In order to use MailerSend for transactional emails, one should have a domain and that needs to be verified. There is a detailed article on their website that explains step by step.

Once done, make sure to activate your account. This will require a clear explanation about the purpose of sending transactional emails, the user's background on running the associated business. In short, one needs to prove their credibility for the account to be activated.

Once the initial setup steps are complete, navigate to Integrations on the left pane. You should see a panel to manage your API Tokens.

Here is a detailed article on managing API tokens.

Alright, we completed all the steps from MailerSend end. Now let's head back to our workspace. Navigate to the .env file and add the following ๐Ÿ‘‡

DEFAULT_FROM_EMAIL=<YOUR-EMAIL-ID-FROM-THE-VERIFIED-DOMAIN>
MAILERSEND_API_TOKEN=<YOUR-MAILERSEND-API-TOKEN>

Next, let's configure email related properties in our settings.py. Prior to which, we should install Anymail from PyPi

pip install "django-anymail[mailersend]"

Make sure to add it in under INSTALLED_PACKAGES in settings.py

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "anymail",
 ]

Next, we add a couple of properties towards the end of settings.py

# MailerSend
ANYMAIL = {
    'MAILERSEND_API_TOKEN': os.environ.get("MAILERSEND_API_TOKEN"),
    "MAILERSEND_SENDER_DOMAIN": '<YOUR-VERIFIED-DOMAIN>'
}
EMAIL_BACKEND = "anymail.backends.mailersend.EmailBackend"
# The id used for sending emails
DEFAULT_FROM_EMAIL = os.environ.get("DEFAULT_FROM_EMAIL")
๐Ÿ’ก
Do not forget to add the following to settings.py ๐Ÿ‘‡
from dotenv import load_dotenv
from pathlib import Path

import os

load_dotenv()

If you miss this, the properties from our .env file will not be loaded.

Write code to send email

Now that all properties related to MailerSend are set, let's work on the actual email sending code. In order to do that, let's modify the method in views.py

Update the existing send_motivational_quote as below ๐Ÿ‘‡

from django.core.mail import send_mail
from django.shortcuts import render
from django.template.loader import render_to_string

from dotenv import load_dotenv
import json
import os
import requests

from django.conf import settings

load_dotenv()


def send_motivational_quote(request):
    if request.method == "POST":
        email = request.POST.get("email")  # Extract email from form data
        response_code, response_text = fetch_motivational_quote()

        # Parse the JSON string into a Python object
        try:
            response_text = json.loads(response_text)
        except json.JSONDecodeError:
            # Handle the exception if the response is not a valid JSON string
            message = "Sorry, something went wrong. Please try again."
            return render(request, "home.html", {"message": message})

        # Extracting quote and author
        quote_text = response_text[0]['quote']
        quote_author = response_text[0]['author']

        if response_code != 200:
            message = "Sorry, something went wrong. Please try again."
            return render(request, "home.html", {"message": message})
        else:
            subject = "Your daily dose of inspiration"
            from_email = settings.DEFAULT_FROM_EMAIL
            to_email = email
            body = render_to_string('email_template.html', 
                  {'quote_text': quote_text, 'quote_author': quote_author})
            send_mail(subject, body, from_email, [to_email], html_message=body)
        message = "Check your email to feel inspired!"
    else:
        message = ""

    return render(request, "home.html", {"message": message})


def fetch_motivational_quote():
    """ Invokes API Ninja's API to fetch an inspirational quote

    """
    category = 'inspirational'
    api_url = 'https://api.api-ninjas.com/v1/quotes?category={}'.format(category)
    API_NINJAS_API_KEY = os.environ.get('API_NINJAS_API_KEY')
    response = requests.get(api_url, headers={'X-Api-Key': API_NINJAS_API_KEY})
    return response.status_code, response.text

Let's look the the methods in detail.

The method fetch_motivational_quote fetches a quote from APINinjas using requests by passing the url and the API_KEY. I selected the category as inspirational. Check here for the complete list of options.

The method send_motivational_quote does the following:

  • Checks whether the request method is POST

  • If yes, fetch the value of email from the form

  • Invoke the fetch_motivational_quote to fetch the response code and response text

  • Parse the JSON String to a Python object

  • Extract the quote and author

  • Set appropriate values for subject, body, from and to emails

  • Finally invoke Django's send_mail method by passing these values

  • Return the home page with a success message

Let's now add a html for the email_template so that it looks neat ๐Ÿคฉ. Create a file email_template.html under the folder djangoandmailersend\templates.

Add the following code, again nothing fancy here ๐Ÿ‘‡

<!DOCTYPE html>
<html>
<head>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f4;
            color: #333;
            line-height: 1.6;
        }
        .container {
            max-width: 600px;
            margin: 20px auto;
            padding: 20px;
            background: #fff;
            border-radius: 10px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
        }
        .header {
            text-align: center;
            color: #444;
        }
        .quote {
            font-style: italic;
            color: #555;
            padding: 20px;
            border-left: 3px solid #007bff;
        }
        .author {
            text-align: right;
            color: #555;
            padding: 10px;
        }
        .footer {
            text-align: center;
            margin-top: 20px;
            color: #666;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>Your Daily Dose of Inspiration</h1>
        </div>
        <div class="quote">
            "{{ quote_text }}"
        </div>
        <div class="author">
            - {{ quote_author }}
        </div>
        <div class="footer">
            <p>Stay inspired!</p>
        </div>
    </div>
</body>
</html>

Time to see it work โ˜บ

I gave my email id in the home page, and I received an email. Woohoo ๐Ÿ˜

Conclusion

In this post, we saw how to integrate Django and MailerSend. The key point in order to use a product like MailerSend is that one needs to own and verify a domain and ensure they use it to send appropriate transactional or marketing emails only.

Since I use MailerSend to send transactional emails in Wobu, I ensured all the setup steps are complete.

Here is my GitHub link if you are interested to take a look at the complete code.

๐Ÿ’ก
I deployed the code on Render and here is the link of my app. Feel free to play around and share with me if you like the quotes you receive ๐Ÿง˜โ€โ™€๏ธ. You will receive an email from

References

How to add and verify your sending domain

Django email integration for MailerSend

ย