Cat

Django 44. Отправка электронной почты в фоновом режиме

В этом посте мы используем подключенный в прошлом посте Celery для отправки письма со ссылкой подтверждения регистрации в фоновом режиме.

Все статьи

Icon Link

Дополнительные материалы

Icon Link

Реклама

Icon Link
Сайт на Django proDream 25 Июль 2024 Просмотров: 318

В этом посте мы используем подключенный в прошлом посте Celery для отправки письма со ссылкой подтверждения регистрации в фоновом режиме.

Делается это достаточно просто — написанием функции с декоратором Celery-задачи. Однако, стоит отметить, что с большой вероятностью потребуются и другие функции отправки почты, различающиеся содержимым письма. Чтобы не плодить однотипный код, мы применим ООП. Мы уже подготовили всё для этого в посте "Django 39. Капча и подтверждение регистрации по email".

 

Включаем отправку в фоне.

Класс SendEmail.

В директории приложения user_app откроем файл tasks.py и найдём класс SendEmail.

Конструктор __init__ класса SendEmail принимает в аргументах объект класса User. Однако, через Celery нельзя отправить в задачу объект класса без предварительной сериализации его в JSON и последующей десериализации. Поэтому изменим аргумент на user_id с числовым типом данных, а в поле класса self.user будем получать объект пользователя из базы данных:

def __init__(self, user_id: int):  
    self.user = User.objects.get(pk=user_id)

 

Ниже в этом же классе у нас есть метод send_activate_email, который вызывает отправку почты выбранному пользователю. В этом методе переменная message хранит строку с текстом письма. Отправлять простой текст не солидно, поэтому лучше использовать красивый шаблон письма.

Перед созданием переменной message создадим переменную context, в которой будет словарь:

context = {  
    "reset_password_url": reset_password_url,  
    "current_site": self.current_site,  
    "title": subject,  
}

 

В контекст помещаем следующие данные:

  • reset_password_url — Путь для сброса пароля.
  • current_site — Домен сайта.
  • title — Заголовок письма.

Затем удаляем содержимое переменной message и вызываем функцию render_to_string, которая позволяет преобразовать HTML-шаблон в строку для отправки по почте. В функцию передаем два аргумента:

  • template_name — Путь до HTML-файла в директории шаблонов.
  • context — Созданный выше словарь контекста.

В вызове метода .email_user у пользователя добавим ещё один аргумент — html_message и присвоим ему message.

Код класса:

from django.contrib.auth.models import User  
from django.contrib.auth.tokens import default_token_generator  
from django.contrib.sites.models import Site  
from django.template.loader import render_to_string  
from django.urls import reverse_lazy  
from django.utils.http import urlsafe_base64_encode  


class SendEmail:  
    def __init__(self, user_id: int):  
        self.user = User.objects.get(pk=user_id)  
        self.current_site = Site.objects.get_current().domain  
        self.token = default_token_generator.make_token(self.user)  
        self.uid = urlsafe_base64_encode(str(self.user.pk).encode())  

    def send_activate_email(self):  
        reset_password_url = reverse_lazy(  
            "user_app:signup_confirm", kwargs={"uidb64": self.uid, "token": self.token}  
        )  
        subject = f"Активация аккаунта на сайте {self.current_site}"  
        context = {  
            "reset_password_url": reset_password_url,  
            "current_site": self.current_site,  
            "title": subject,  
        }  
        message = render_to_string(template_name="email/activate_email.html", context=context)  

        self.user.email_user(subject=subject, message=message, html_message=message)

 

В директории с шаблонами создадим директорию email, а в ней файл activate_email.html.

Пример простейшего шаблона:

<!doctype html>  
<html lang="ru">  
<head>  
    <meta charset="UTF-8">  
    <meta name="viewport"  
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">  
    <meta http-equiv="X-UA-Compatible" content="ie=edge">  
    <title>{{ title }}</title>  
</head>  
<body>  
<h2>Благодарим за регистрацию на сайте {{ current_site }}.</h2>  
<p>Для активации учётной записи, пожалуйста перейдите по ссылке:</p>  
<a href="https://{{ current_site }}{{ reset_password_url }}">Подтвердить регистрацию.</a>  
</body>  
</html>

 

Обратите внимание! Чтобы в письме отображался CSS, стили необходимо прописывать прямо в HTML-файле, либо использовать библиотеку django-inlinecss. Дело в том, что большинство почтовых клиентов не поддерживают загрузку внешних стилей и отрисовывают только то, что есть в теле письма.

 

Задача отправки письма.

Ниже под классом найдём функцию activate_email_task. В ней также изменим тип получаемых данных у аргумента user и добавим над функцией декоратор @shared_task(). Этот декоратор сообщает Celery о том, что связанная с ним функция является задачей и добавляет в функцию новый метод .delay().

from celery import shared_task


@shared_task()  
def activate_email_task(user_id: int):  
    send_email = SendEmail(user_id=user_id)  
    send_email.send_activate_email()

 

Доработка представления.

Откроем файл views.py и найдём представление для регистрации CustomRegistrationView. В методе form_valid мы вызывали activate_email_task, передавая в функцию весь объект пользователя. Теперь, чтобы всё заработало, необходимо у функции вызвать метод .delay() и передать в него id пользователя:

def form_valid(self, form):  
    user: User = form.save()  
    user.is_active = False  
    user.save()  
    activate_email_task.delay(user.pk)  
    return HttpResponseRedirect(self.get_success_url())

 

Готово! Теперь можно включить Django и Celery и проверить, как отправляется почта.

 

Заключение.

Получился небольшой пост, в котором по большому счёту всё было готово заранее, оставалось только внести правки. Бонусом было рассказано, как отправлять почту с HTML-шаблоном. В следующем посте займёмся более интересным делом — созданием функционала подписки на email-рассылку новостей сайта.

Автор

    Нет комментариев

    Реклама