Djoser not sending emails when default emails are overridden

Viewed 481

I have a DRF project that uses Djoser for authentication. The plan is to override Djoser's default activation email and send something an HTML template email. Here's what I have:

# settings.py

DJOSER = {
    "SEND_ACTIVATION_EMAIL": True,
    "ACTIVATION_URL": "activate/{uid}/{token}",
    "EMAIL": {"activation": "base.emails.ActivationEmail"},
}
# base/emails

from django.contrib.auth.tokens import default_token_generator
from djoser import email, utils
from djoser.conf import settings


class ActivationEmail(email.ActivationEmail):
    template_name = "emails/activation.html"

    def get_context_data(self):
        # ActivationEmail can be deleted
        context = super().get_context_data()

        user = context.get("user")
        context["first_name"] = user.first_name
        context["uid"] = utils.encode_uid(user.pk)
        context["token"] = default_token_generator.make_token(user)
        context["url"] = settings.ACTIVATION_URL.format(**context)
        return context

# templates/emails/activation.html

{% extends "emails/base.html" %}

{% load static %}

{% block subject %}Account Activation on Dummy{% endblock %}

{% block heading %}
    Account Activation
{% endblock %}

{% block content %}
    You're receiving this email because you need to finish activation process.
    Please click on the button below to activate your account:

    <br><br><br>
    <a
        href="{{ url }}"
        target="_blank"
        style="
            text-decoration: none;
            font-family: lato, 'helvetica neue', helvetica, arial, sans-serif;
            font-size: 20px;
            color: #fff;
            background-color: #0b913a;
            padding: 10px 25px;
            border-radius: 5px;
        "
        >Activate</a
    >
    <br><br><br>

    Best Regards,<br>
    The DUMMY Team

    <br><br>
    <p
        style="
            margin: 0;
            -webkit-text-size-adjust: none;
            -ms-text-size-adjust: none;
            mso-line-height-rule: exactly;
            font-size: 14px;
            font-family: lato, 'helvetica neue', helvetica, arial, sans-serif;
            line-height: 21px;
            color: #666666;
        "
    >
        If you did not make this request, reply to this email or write to info@dummy.com so we can 
        look into a possible attempt to impersonate you.
    </p>
{% endblock %}

Everything seems correct, but the email doesn't send. I check the inbox and see nothing. But when I remove the remove the EMAIL setting pointing to the custom email, it sends the default Djoser activation email. What's wrong?

2 Answers

I just compared it with my custom activation email with Djoser in my project and your configuration looks pretty similar.

In ActivationEmail there's a send method. You might want to override it and add some logs. Maybe that will help.

    def send(self, to, *args, **kwargs):
        logger.info(f"Sending activation mail to {to}")
        try:
            super().send(to, *args, **kwargs)
        except:
            logger.exception(f"Couldn't send mail to {to}")
            raise
        logger.info(f"Activation mail sent successfully to {to}")

Anyway, just write a test. In your test you can check if email has been sent using

self.assertEqual(len(mail.outbox), 1)

where mail comes from.

from django.core import mail

Ok, I just faced the same problem. The problem is that when you add inline CSS, the template doesn't load. So under ActivationEmail class, override the send method and use send_mail and render_to_string.

# app/emails.py
from djoser import email
from djoser import utils
from djoser.conf import settings
from django.contrib.auth.tokens import default_token_generator

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


class ActivationEmail(email.ActivationEmail):
    template_name = 'mail/activation.html'

    def get_context_data(self):
        # ActivationEmail can be deleted
        context = super().get_context_data()

        user = context.get("user")
        context['user'] = user.name
        context["uid"] = utils.encode_uid(user.pk)
        context["token"] = default_token_generator.make_token(user)
        context["url"] = settings.ACTIVATION_URL.format(**context)
        return context

    def send(self, to, *args, **kwargs):

        self.to = to
        self.cc = kwargs.pop('cc', [])
        self.bcc = kwargs.pop('bcc', [])
        self.reply_to = kwargs.pop('reply_to', [])
        # self.from_email = kwargs.pop(
        #     'from_email', settings.DEFAULT_FROM_EMAIL
        # )

        context = self.get_context_data()

        html_mail = render_to_string(self.template_name, context=context)

        send_mail(subject=f"Complete your activation on {context.get('site_name', 'strings')}", 
                        message=f"Please go to the following page to activate account {context.get('protocol')}://{ context.get('domain') }/{context.get('url')}",
                        from_email=None, 
                        recipient_list=[self.to], 
                        html_message=html_mail,
                        fail_silently=True)

        # super().send()

Now add this to settings:

DJOSER = {
    "SEND_ACTIVATION_EMAIL": True,
    "ACTIVATION_URL": "activate/{uid}/{token}",
    "EMAIL": {"activation": "app.emails.ActivationEmail"}, # <--- change the path to whatever yours is
}
Related