Django 44. Отправка электронной почты в фоновом режиме
В этом посте мы используем подключенный в прошлом посте Celery для отправки письма со ссылкой подтверждения регистрации в фоновом режиме.
Дополнительные материалы
Для скачивания материалов необходимо войти или зарегистрироваться
Файлы также можно получить в Telegram-боте по коду: 413358
Реклама
В этом посте мы используем подключенный в прошлом посте 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-рассылку новостей сайта.
Все статьи