Отправка электронных писем
Хотя Python предоставляет интерфейс для отправки почты через модуль smtplib, Django предоставляет несколько лёгких обёртков поверх него. Эти обёртки предоставляются для ускорения отправки писем, для помощи в тестировании отправки писем во время разработки и для предоставления поддержки платформам, которые не могут использовать SMTP.
Код находится в модуле django.core.mail.
Быстрый пример
В двух строках:
from django.core.mail import send_mail
send_mail(
"Subject here",
"Here is the message.",
"from@example.com",
["to@example.com"],
fail_silently=False,
)
Почта отправляется, используя хост и порт SMTP, указанные в настройках EMAIL_HOST и EMAIL_PORT. Настройки EMAIL_HOST_USER и EMAIL_HOST_PASSWORD, если они заданы, используются для аутентификации на сервере SMTP, а настройки EMAIL_USE_TLS и EMAIL_USE_SSL контролируют использование защищённого соединения.
Примечание
Кодировка символов отправляемого электронного письма с помощью django.core.mail будет установлена в значение вашей настройки DEFAULT_CHARSET.
send_mail()
-
send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None, connection=None, html_message=None)
В большинстве случаев вы можете отправлять электронные письма, используя django.core.mail.send_mail().
Параметры subject, message, from_email и recipient_list являются обязательными.
-
subject: Строка. -
message: Строка. -
from_email: Строка. ЕслиNone, Django будет использовать значение настройкиDEFAULT_FROM_EMAIL. -
recipient_list: Список строк, каждая из которых является адресом электронной почты. Каждый членrecipient_listувидит других получателей в поле «Кому» сообщения электронной почты. -
fail_silently: Булево значение. Когда этоFalse,send_mail()будет генерировать исключениеsmtplib.SMTPException, если произошла ошибка. См. документацию поsmtplibдля списка возможных исключений, все из которых являются подклассамиSMTPException. -
auth_user: Необязательное имя пользователя для аутентификации на сервере SMTP. Если это не указано, Django будет использовать значение настройкиEMAIL_HOST_USER. -
auth_password: Необязательный пароль для аутентификации на сервере SMTP. Если это не указано, Django будет использовать значение настройкиEMAIL_HOST_PASSWORD. -
connection: Необязательный бэкенд электронной почты для отправки почты. Если не указано, будет использована инстанция по умолчанию. См. документацию по Бэкендам электронной почты для получения более подробной информации. -
html_message: Еслиhtml_messageпредоставлен, результирующее электронное письмо будет представлять собой email типа multipart/alternative сmessageкак типом содержимого text/plain иhtml_messageкак типом содержимого text/html.
Возвращаемое значение будет равно количеству успешно доставленных сообщений (которое может быть 0 или 1, поскольку она может отправить только одно сообщение).
send_mass_mail()
-
send_mass_mail(datatuple, fail_silently=False, auth_user=None, auth_password=None, connection=None)
django.core.mail.send_mass_mail() предназначена для обработки массовой рассылки писем.
datatuple представляет собой кортеж, в котором каждый элемент имеет следующий формат:
(subject, message, from_email, recipient_list)
fail_silently, auth_user и auth_password имеют те же функции, что и в send_mail().
Каждый отдельный элемент datatuple приводит к отдельному сообщению электронной почты. Как и в send_mail(), получатели в одном recipient_list все увидят другие адреса в поле «Кому» электронных сообщений.
Например, следующий код отправит два разных сообщения двум различным группам получателей; однако будет открыто только одно подключение к почтовому серверу:
message1 = (
"Subject here",
"Here is the message",
"from@example.com",
["first@example.com", "other@example.com"],
)
message2 = (
"Another Subject",
"Here is another message",
"from@example.com",
["second@test.com"],
)
send_mass_mail((message1, message2), fail_silently=False)
Возвращаемое значение будет равно количеству успешно доставленных сообщений.
send_mass_mail() vs. send_mail()
Главное различие между send_mass_mail() и send_mail() заключается в том, что send_mail() открывает соединение с почтовым сервером каждый раз при выполнении, в то время как send_mass_mail() использует одно соединение для всех сообщений. Это делает send_mass_mail() немного более эффективным.
mail_admins()
-
mail_admins(subject, message, fail_silently=False, connection=None, html_message=None)
django.core.mail.mail_admins() — это сокращение для отправки электронного письма администраторам сайта, как определено в настройке ADMINS.
mail_admins() добавляет префикс к теме, используя значение настройки EMAIL_SUBJECT_PREFIX, которое по умолчанию равно "[Django] ".
Заголовок «От кого» электронного письма будет равен значению настройки SERVER_EMAIL.
Эта функция существует для удобства и читаемости.
Если html_message предоставлен, результирующее электронное письмо будет представлять собой email типа multipart/alternative с message как типом содержимого text/plain и html_message как типом содержимого text/html.
mail_managers()
-
mail_managers(subject, message, fail_silently=False, connection=None, html_message=None)
django.core.mail.mail_managers() аналогичен mail_admins(), за исключением того, что он отправляет электронное письмо менеджерам сайта, как определено в настройке MANAGERS.
Примеры
Это отправляет единственное электронное письмо на john@example.com и jane@example.com, и оба они будут отображаться в поле «Кому»:
send_mail(
"Subject",
"Message.",
"from@example.com",
["john@example.com", "jane@example.com"],
)
Это отправляет сообщение на john@example.com и jane@example.com, при этом каждый получит отдельное электронное письмо:
datatuple = (
("Subject", "Message.", "from@example.com", ["john@example.com"]),
("Subject", "Message.", "from@example.com", ["jane@example.com"]),
)
send_mass_mail(datatuple)
Предотвращение инъекции заголовков
Инъекция заголовков — это эксплойт, при котором злоумышленник вставляет дополнительные заголовки электронной почты, чтобы контролировать поля «Кому» и «От кого» в сообщениях электронной почты, генерируемых вашими скриптами.
Все функции Django для отправки электронных писем, описанные выше, защищают от инъекции заголовков, запрещая новые строки в значениях заголовков. Если какой-либо subject, from_email или recipient_list содержит новую строку (в формате Unix, Windows или Mac), функция отправки электронного письма (например, send_mail()) сгенерирует исключение django.core.mail.BadHeaderError (подкласс ValueError) и, следовательно, не отправит электронное письмо. Ваша обязанность — валидировать все данные перед передачей их в функции отправки электронных писем.
Если message содержит заголовки в начале строки, заголовки будут напечатаны в качестве первой части электронного сообщения.
Вот пример представления, которое получает subject, message и from_email из данных POST запроса, отправляет их на admin@example.com и перенаправляет на «/contact/thanks/» по завершении:
from django.core.mail import BadHeaderError, send_mail
from django.http import HttpResponse, HttpResponseRedirect
def send_email(request):
subject = request.POST.get("subject", "")
message = request.POST.get("message", "")
from_email = request.POST.get("from_email", "")
if subject and message and from_email:
try:
send_mail(subject, message, from_email, ["admin@example.com"])
except BadHeaderError:
return HttpResponse("Invalid header found.")
return HttpResponseRedirect("/contact/thanks/")
else:
# In reality we'd use a form class
# to get proper validation errors.
return HttpResponse("Make sure all fields are entered and valid.")
Класс EmailMessage
Функции Django send_mail() и send_mass_mail() фактически являются тонкими оболочками, которые используют класс EmailMessage.
Не все возможности класса EmailMessage доступны через функции send_mail() и связанные обертки. Если вы хотите использовать расширенные возможности, такие как BCC-получатели, вложения файлов или многочастные письма, вам нужно создать экземпляры EmailMessage напрямую.
Примечание
Это особенность дизайна. Функции send_mail() и связанные функции изначально были единственным интерфейсом, предоставляемым Django. Однако со временем список параметров, которые они принимали, постоянно рос. Это имело смысл для перехода к более объектно-ориентированному проектированию электронных сообщений и сохранения оригинальных функций только для обратной совместимости.
Класс EmailMessage отвечает за создание самого электронного сообщения. Сервер отправки писем backend отвечает за отправку письма.
Для удобства класс EmailMessage предоставляет метод send() для отправки одного письма. Если вам нужно отправить несколько сообщений, API бекенда предоставляет альтернативный вариант.
EmailMessage Объекты
-
class EmailMessage
Класс EmailMessage инициализируется следующими параметрами (в указанном порядке, если используются позиционные аргументы). Все параметры являются необязательными и могут быть установлены в любое время до вызова метода send().
-
subject: Тема письма. -
body: Текстовое тело сообщения. Это должно быть простое текстовое сообщение. -
from_email: Адрес отправителя. Допускаются какfred@example.com, так и"Fred" <fred@example.com>формы. Если опущен, используется настройкаDEFAULT_FROM_EMAIL. -
to: Список или кортеж адресов получателей. -
bcc: Список или кортеж адресов, используемых в заголовке «Bcc» при отправке письма. -
connection: Экземпляр бекенда электронной почты. Используйте этот параметр, если вы хотите использовать одно и то же соединение для нескольких сообщений. Если опущен, новое соединение создается при вызовеsend(). -
attachments: Список вложений для добавления в сообщение. Это могут быть экземплярыMIMEBaseили кортежи(filename, content, mimetype). -
headers: Словарь дополнительных заголовков для добавления к сообщению. Ключи — имя заголовка, значения — значения заголовка. От пользователя требуется обеспечить, что имена и значения заголовков имеют правильный формат для электронного сообщения. Соответствующее атрибут —extra_headers. -
cc: Список или кортеж адресов получателей, используемых в заголовке «Cc» при отправке письма. -
reply_to: Список или кортеж адресов получателей, используемых в заголовке «Reply-To» при отправке письма.
Например:
from django.core.mail import EmailMessage
email = EmailMessage(
"Hello",
"Body goes here",
"from@example.com",
["to1@example.com", "to2@example.com"],
["bcc@example.com"],
reply_to=["another@example.com"],
headers={"Message-ID": "foo"},
)
У класса есть следующие методы:
-
send(fail_silently=False)отправляет сообщение. Если при создании письма было указано соединение, будет использовано это соединение. В противном случае будет создан и использован экземпляр бекенда по умолчанию. Если ключевой аргументfail_silentlyравенTrue, исключения, возникающие при отправке сообщения, будут подавлены. Пустой список получателей не вызовет исключения. Будет возвращено1при успешной отправке сообщения, в противном случае0. -
message()строит объектdjango.core.mail.SafeMIMEText(подкласс класса Python’sMIMEText) или объектdjango.core.mail.SafeMIMEMultipart, содержащий отправляемое сообщение. Если вам когда-нибудь нужно расширить классEmailMessage, вам, вероятно, нужно будет переопределить этот метод, чтобы поместить необходимое содержимое в объект MIME. -
recipients()возвращает список всех получателей сообщения, независимо от того, записаны ли они в атрибутахto,ccилиbcc. Это еще один метод, который вам может потребоваться переопределить при наследовании, потому что SMTP-серверу необходимо сообщить полный список получателей при отправке сообщения. Если вы добавите другой способ указать получателей в своем классе, они также должны возвращаться из этого метода. -
attach()создаёт новое вложение файла и добавляет его к сообщению. Существуют два способа вызоваattach():- Вы можете передать единственный аргумент, который является экземпляром
MIMEBase. Это будет вставлено непосредственно в результирующее сообщение. -
В качестве альтернативы можно передать
attach()три аргумента:filename,contentиmimetype.filename— имя вложения файла, как оно будет отображаться в письме,content— данные, которые будут содержаться внутри вложения, аmimetype— необязательный тип MIME для вложения. Если вы опуститеmimetype, тип MIME содержимого будет угадан из имени файла вложения.Например:
message.attach("design.png", img_data, "image/png")Если вы укажете тип MIME message/rfc822, он также будет принимать
django.core.mail.EmailMessageиemail.message.Message.Для типов MIME, начинающихся с text/, ожидается, что содержимое будет строкой. Двоичные данные будут декодированы с использованием UTF-8, и если это не удастся, тип MIME будет изменён на application/octet-stream, а данные будут прикреплены без изменений.
Кроме того, вложения типа message/rfc822 больше не будут кодироваться в base64 в нарушение RFC 2046#section-5.2.1, что может вызвать проблемы с отображением вложений в Evolution и Thunderbird.
- Вы можете передать единственный аргумент, который является экземпляром
-
attach_file()создаёт новое вложение с использованием файла из вашей файловой системы. Вызовите его с путем к файлу для вложения и, необязательно, типом MIME для вложения. Если тип MIME опущен, он будет угадан из имени файла. Вы можете использовать его так:message.attach_file("/images/weather_map.png")Для типов MIME, начинающихся с text/, обработка двоичных данных аналогична
attach().
Отправка альтернативных типов содержимого
Полезно включать несколько версий содержимого в электронное письмо; классический пример — отправка текстовой и HTML-версий сообщения. В библиотеке электронных писем Django вы можете сделать это с помощью класса EmailMultiAlternatives. Этот подкласс EmailMessage имеет метод attach_alternative() для включения дополнительных версий тела сообщения в электронное письмо. Все остальные методы (включая инициализацию класса) наследуются напрямую от EmailMessage.
Для отправки комбинации текста и HTML можно написать:
from django.core.mail import EmailMultiAlternatives subject, from_email, to = "hello", "from@example.com", "to@example.com" text_content = "This is an important message." html_content = "<p>This is an <strong>important</strong> message.</p>" msg = EmailMultiAlternatives(subject, text_content, from_email, [to]) msg.attach_alternative(html_content, "text/html") msg.send()
По умолчанию тип MIME параметра body в EmailMessage равен "text/plain". Рекомендуется оставить его без изменений, так как это гарантирует, что любой получатель сможет прочитать электронное письмо независимо от почтового клиента. Однако если вы уверены, что ваши получатели могут обрабатывать альтернативный тип содержимого, вы можете использовать атрибут content_subtype класса EmailMessage для изменения основного типа содержимого. Основной тип всегда будет "text", но вы можете изменить подтип. Например:
msg = EmailMessage(subject, html_content, from_email, [to]) msg.content_subtype = "html" # Main content is now text/html msg.send()
Бекенды электронной почты
Фактическая отправка электронного письма обрабатывается бекендом электронной почты.
Класс бекенда электронной почты имеет следующие методы:
-
open()создает долговременное соединение для отправки писем. -
close()закрывает текущее соединение для отправки писем. -
send_messages(email_messages)отправляет список объектовEmailMessage. Если соединение не открыто, этот вызов неявно откроет соединение и закроет его после отправки писем. Если соединение уже открыто, оно останется открытым после отправки писем.
Он также может использоваться как менеджер контекста, который автоматически вызовет open() и close() по мере необходимости:
from django.core import mail
with mail.get_connection() as connection:
mail.EmailMessage(
subject1,
body1,
from1,
[to1],
connection=connection,
).send()
mail.EmailMessage(
subject2,
body2,
from2,
[to2],
connection=connection,
).send()
Получение экземпляра бекенда электронной почты
Функция get_connection() в django.core.mail возвращает экземпляр почтового бэкенда, который вы можете использовать.
-
get_connection(backend=None, fail_silently=False, *args, **kwargs)
По умолчанию вызов get_connection() вернёт экземпляр почтового бэкенда, указанного в EMAIL_BACKEND. Если вы укажете аргумент backend, будет создан экземпляр этого бэкенда.
Аргумент fail_silently управляет тем, как бэкенд должен обрабатывать ошибки. Если fail_silently имеет значение True, исключения во время процесса отправки писем будут молча игнорироваться.
Все остальные аргументы передаются непосредственно в конструктор почтового бэкенда.
Django поставляется с несколькими бэкендами для отправки писем. За исключением SMTP бэкенда (который является по умолчанию), эти бэкенды полезны только во время тестирования и разработки. Если у вас есть особые требования к отправке писем, вы можете написать собственный почтовый бэкенд.
SMTP бэкенд
-
class backends.smtp.EmailBackend(host=None, port=None, username=None, password=None, use_tls=None, fail_silently=False, use_ssl=None, timeout=None, ssl_keyfile=None, ssl_certfile=None, **kwargs) -
Это бэкенд по умолчанию. Письма будут отправляться через SMTP-сервер.
Значение каждого аргумента извлекается из соответствующей настройки, если аргумент
None:-
host:EMAIL_HOST -
port:EMAIL_PORT -
username:EMAIL_HOST_USER -
password:EMAIL_HOST_PASSWORD -
use_tls:EMAIL_USE_TLS -
use_ssl:EMAIL_USE_SSL -
timeout:EMAIL_TIMEOUT -
ssl_keyfile:EMAIL_SSL_KEYFILE -
ssl_certfile:EMAIL_SSL_CERTFILE
SMTP бэкенд является конфигурацией по умолчанию, унаследованной от Django. Если вы хотите указать его явно, поместите следующее в свои настройки:
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
Если не указано, значение по умолчанию для
timeoutбудет таким, как предоставляетsocket.getdefaulttimeout(), которое по умолчанию равноNone(нет таймаута). -
Консольный бэкенд
Вместо отправки реальных писем, бэкенд консоли просто записывает письма, которые были бы отправлены в стандартный вывод. По умолчанию, консольный бэкенд записывает в stdout. Вы можете использовать другой объект типа поток, передав аргумент stream при создании соединения.
Чтобы указать этот бэкенд, поместите следующее в свои настройки:
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
Этот бэкенд не предназначен для использования в производственной среде – он предоставляется для удобства использования во время разработки.
Файловый бэкенд
Файловый бэкенд записывает письма в файл. Для каждой новой сессии, открытой в этом бэкенде, создаётся новый файл. Директория, в которую записываются файлы, берётся либо из настройки EMAIL_FILE_PATH, либо из аргумента file_path при создании соединения с помощью get_connection().
Чтобы указать этот бэкенд, поместите следующее в свои настройки:
EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend" EMAIL_FILE_PATH = "/tmp/app-messages" # change this to a proper location
Этот бэкенд не предназначен для использования в производственной среде – он предоставляется для удобства использования во время разработки.
Бэкенд памяти
Бэкенд 'locmem' хранит сообщения в специальном атрибуте модуля django.core.mail. Атрибут outbox создаётся при отправке первого сообщения. Это список с экземпляром EmailMessage для каждого сообщения, которое должно быть отправлено.
Чтобы указать этот бэкенд, поместите следующее в свои настройки:
EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
Этот бэкенд не предназначен для использования в производственной среде – он предоставляется для удобства использования во время разработки и тестирования.
Тестовый запуск Django автоматически использует этот бэкенд для тестирования.
Бэкенд-заглушка
Как следует из названия, бэкенд-заглушка ничего не делает с вашими сообщениями. Чтобы указать этот бэкенд, поместите следующее в свои настройки:
EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
Этот бэкенд не предназначен для использования в производственной среде – он предоставляется для удобства использования во время разработки.
Определение пользовательского почтового бэкенда
Если вам нужно изменить способ отправки писем, вы можете написать свой собственный почтовый бэкенд. Настройка EMAIL_BACKEND в файле настроек — это путь импорта Python для вашего класса бэкенда.
Пользовательские почтовые бэкенды должны наследоваться от BaseEmailBackend, который находится в модуле django.core.mail.backends.base. Пользовательский почтовый бэкенд должен реализовать метод send_messages(email_messages). Этот метод получает список экземпляров EmailMessage и возвращает количество успешно доставленных сообщений. Если ваш бэкенд имеет какое-либо понятие о сохраняемой сессии или соединении, вы также должны реализовать методы open() и close(). Обратитесь к smtp.EmailBackend для ссылки на реализацию.
Отправка нескольких писем
Установление и закрытие SMTP-соединения (или любого другого сетевого соединения) — это дорогостоящий процесс. Если вам нужно отправить много писем, имеет смысл повторно использовать SMTP-соединение вместо создания и уничтожения соединения каждый раз, когда вы хотите отправить письмо.
Существует два способа указать почтовому бэкенду повторно использовать соединение.
Во-первых, вы можете использовать метод send_messages(). send_messages() принимает список экземпляров EmailMessage (или подклассов) и отправляет их все с помощью одного соединения.
Например, если у вас есть функция get_notification_email(), которая возвращает список объектов EmailMessage, представляющих некоторые периодические письма, которые вы хотите отправить, вы можете отправить эти письма с помощью одного вызова send_messages:
from django.core import mail connection = mail.get_connection() # Use default email connection messages = get_notification_email() connection.send_messages(messages)
В этом примере вызов send_messages() открывает соединение на бэкенде, отправляет список сообщений и затем закрывает соединение.
Второй подход заключается в использовании методов open() и close() почтового бэкенда для ручного управления соединением. send_messages() не будет вручную открывать или закрывать соединение, если оно уже открыто, поэтому если вы вручную откроете соединение, вы можете контролировать, когда оно будет закрыто. Например:
from django.core import mail
connection = mail.get_connection()
# Manually open the connection
connection.open()
# Construct an email message that uses the connection
email1 = mail.EmailMessage(
"Hello",
"Body goes here",
"from@example.com",
["to1@example.com"],
connection=connection,
)
email1.send() # Send the email
# Construct two more messages
email2 = mail.EmailMessage(
"Hello",
"Body goes here",
"from@example.com",
["to2@example.com"],
)
email3 = mail.EmailMessage(
"Hello",
"Body goes here",
"from@example.com",
["to3@example.com"],
)
# Send the two emails in a single call -
connection.send_messages([email2, email3])
# The connection was already open so send_messages() doesn't close it.
# We need to manually close the connection.
connection.close()
Настройка электронной почты для разработки
Иногда вам не нужно, чтобы Django вообще отправлял письма. Например, при разработке веб-сайта, вероятно, не нужно отправлять тысячи писем, но вы можете захотеть проверить, что письма отправляются нужным людям в нужных условиях и что эти письма содержат правильное содержимое.
Самый простой способ настроить почту для разработки — использовать почтовый бэкенд консоли. Этот бэкенд перенаправляет всю почту в stdout, позволяя вам просмотреть содержимое писем.
Бэкенд электронной почты файлов также может быть полезен во время разработки — этот бэкенд выводит содержимое каждого SMTP-соединения в файл, который можно просмотреть по своему усмотрению.
Другой подход заключается в использовании «глупого» SMTP-сервера, который получает письма локально и отображает их в терминале, но фактически ничего не отправляет. Пакет aiosmtpd предоставляет способ достижения этого:
python -m pip install aiosmtpd python -m aiosmtpd -n -l localhost:8025
Эта команда запустит минимальный SMTP-сервер, прослушивающий порт 8025 на localhost. Этот сервер выводит в стандартный вывод все заголовки писем и тело письма. Затем вам нужно только установить EMAIL_HOST и EMAIL_PORT соответственно. Более подробное обсуждение параметров SMTP-сервера см. в документации модуля aiosmtpd.
Дополнительную информацию о тестировании отправки писем в вашем приложении см. в разделе Услуги электронной почты документации по тестированию.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/5.0/topics/email/