Tracking Email (One Pixel)-Django REST framework

manoj adhikari
5 min readMay 23, 2021

Hold on ! We will make it happen !!

Cheers to you , if you were desperately looking forward to seeing how it’s done to track whether the targeted users have opened or not the email that you have sent. Sounds cool right? Assuming that readers are familiar to the basic concepts of django REST framework I am skipping the minor details but I’ll reference enough sources to make things sense more better , in case required.

Tracking Email

How it’s Done ???

We will be sending the html template along with other mail contents to the target user, the html template contains some image tagged as

<img src="{{url_is}}" height="40px" width="40px"/>

Here the context {{url_is}}, is the source of the image which is also the url route of request which get hit when the image get rendered when target user opens the email . Now the request is called and the email open or not status of the target user is now known .

Now image can be simply made invisible to make target user not suspicious by setting the height and the width value of the image to 0px or 1px as dot.

Let’s Get Started ..

Alright let’s create a django app called track in the project directory.

django-admin startapp track

1. Creating Model for Target User

Let’s create a target user model with required fields in models.py inside track folder, for the target user to whom we will be sending the email

from django.db import modelsfrom django.db.models.fields import CharField, EmailField, UUIDField# Create your models here.class UserModel(models.Model):     email = EmailField(max_length = 254 , blank= True , null=True)     status = models.BooleanField(default=False)     def __str__(self):
return self.email

Here, email field references the email of target user , status field is to know status of target user whether he/she opened or not the mail.

Now make sure that your model is registered in admin.py

admin.site.register(UserModel)

Also make sure that app track is added in INSTALLED_APPS section of settings.py of the project. Once done , run the migrations, createsuper and be sure from django admin login that model and it’s are fields are well created.

2. Email Template

Here I assume that readers are familiar with the concept of sending email with templates in django rest-framework through SMTP. So I am skipping it for now. I will be write article on it soon as well.

Following to that , this is how our mail_template.html is ,

<!DOCTYPE html><html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>This is title </title>
</head>
<body>
Hello ,
<p>Some message here </p>
<p>{{user}}<br />Image Link & Image</p>
<p>Link: {{url_is}}</p>
<img src="{{url_is}}" height="40px" width="40px"/>
</body>
</html>

3. Writing Views

Let’s write some requests in views.py which helps us to send the template in mail and then check the status whether the email has been open or not by the target user.

Imports in views.py include:

from django.shortcuts import renderfrom rest_framework.views import APIViewfrom rest_framework.response import Responsefrom rest_framework import statusfrom .models import *from django.core.mail import EmailMultiAlternativesfrom django.http import HttpResponsefrom PIL import Imagefrom rest_framework.decorators import api_viewfrom django.template import Contextfrom django.template.loader import get_template

3.1 POST Request for Sending Email.

Here the class SendTemplateMailView helps to send the mail to the target user along with html template.

class SendTemplateMailView(APIView):     def post(self, request, *args, **kwargs):          target_user_email = request.data.get('email')           mail_template = get_template("mail_template.html")           context_data_is = dict()           context_data_is["image_url"] =   request.build_absolute_uri(("render_image"))           url_is = context_data_is["image_url"]           context_data_is['url_is']= url_is           html_detail = mail_template.render(context_data_is)           subject, from_email, to = "Greetings !!" ,  'postmaster@manojadhikary.com.np',  [target_user_email]           msg = EmailMultiAlternatives(subject, html_detail, from_email, to)           msg.content_subtype='html'           msg.send()           return Response({"success":True})

3.2 PUT Request for Email Open Status

Here the render_image function is the request that get’s hit when the email is opened and then updates the status field to True. Add the following code just below the SendTemplateMailView class module

@api_view()
def render_image(request):
if request.method =='PUT':
image= Image.new('RGB', (20, 20))
response = HttpResponse(content_type="image/png" , status = status.HTTP_200_OK)
user = UserModel.objects.get(id = 1)
user.status = True
user.save()
image.save(response, "PNG")
return response

In an above code we have a line as

user = UserModel.objects.get(id = 1)

Here we have manually set the id = 1 to make the status update on that particular user for testing purpose.

3.3 Writing Routes

It’s time to route the requests in urls.py. Let’s add the following routes for the two different requests as written in views.

from django.urls import path , includefrom .views import SendTemplateMailView , render_image
urlpatterns = [
path('send/render_image/',render_image, name='render_image'), path('send/', SendTemplateMailView.as_view(), name= 'send_template'),]

Make sure that you have referenced the urls.py of track in the urls.py of the project.

4. Testing — Using Postman

The project must be hosted to work out in rendering of the image. I have hosted it on heroku server (will article it soon as well). The tests can be done through the postman.

First of all we are going to add the target user , we will do form the django admin , we have created this target user to check it’s status field whose id value is 1 such that it gets updated

user = UserModel.objects.get(id = 1) 
Add Target user in the Target UserModel

Now let us ping the send template request from the postman.

POST Request for sending mail

Now let’s check what’s in our inbox..

Opening Sent Email

Cool ! , Now since the email has been open let’s check the status at the django-admin of that user.

Email Open Status

Since the status is ticked , it means True, that email has been open.

FurtherMore, to find the status of each targeted users , we need to follow up dynamic routing for each users and will publish article on it soon .

This is my first ever article ! Clap if you think it’s worth.

Congratulations !! You made it to the end.

Hurray !

For reference github — https://github.com/jonamadk/Django-track-pixel

Happy Coding ..

--

--