Отправка электронных писем
Хотя 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)[source]
Самый простой способ отправить электронное письмо — использовать django.core.mail.send_mail().
Параметры subject, message, from_email и recipient_list являются обязательными.
-
subject: Строка. -
message: Строка. -
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предоставлено, результирующее электронное письмо будет многочастным multipart/alternative письмом сmessageкак text/plain типом содержимого иhtml_messageкак text/html типом содержимого.
Значением возврата будет количество успешно доставленных сообщений (которое может быть 0 или 1, так как она может отправить только одно сообщение).
Параметр html_message был добавлен.
send_mass_mail()
-
send_mass_mail(datatuple, fail_silently=False, auth_user=None, auth_password=None, connection=None)[source]
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)[source]
django.core.mail.mail_admins() — это сокращение для отправки электронного письма администраторам сайта, как определено в настройке ADMINS.
mail_admins() добавляет префикс к теме, используя значение настройки EMAIL_SUBJECT_PREFIX, которое по умолчанию равно "[Django] ".
Заголовок «От кого» электронного письма будет соответствовать значению настройки SERVER_EMAIL.
Этот метод существует для удобства и читаемости.
Если html_message предоставлено, результирующее электронное письмо будет многочастным multipart/alternative письмом с message как text/plain типом содержимого и html_message как text/html типом содержимого.
mail_managers()
-
mail_managers(subject, message, fail_silently=False, connection=None, html_message=None)[source]
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 send_mail, BadHeaderError
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 отвечает за создание самого сообщения электронной почты. Затем бэкенд электронной почты отвечает за отправку письма.
Для удобства EmailMessage предоставляет простой метод send() для отправки одного письма. Если вам нужно отправить несколько сообщений, API бэкенда электронной почты предоставляет альтернативный вариант.
Объекты EmailMessage
-
class EmailMessage[source]
Класс EmailMessage инициализируется следующими параметрами (в заданном порядке, если используются позиционные аргументы). Все параметры необязательны и могут быть заданы в любое время до вызова метода send().
-
subject: Тема письма. -
body: Текстовое тело. Это должно быть простое текстовое сообщение. -
from_email: Адрес отправителя. Допустимы какfred@example.com, так иFred <fred@example.com>формы. Если параметр опущен, используется значение настройкиDEFAULT_FROM_EMAIL. -
to: Список или кортеж адресов получателей. -
bcc: Список или кортеж адресов, используемых в заголовке «Bcc» при отправке письма. -
connection: Экземпляр бэкенда электронной почты. Используйте этот параметр, если вы хотите использовать одно и то же соединение для нескольких сообщений. Если этот параметр опущен, новое соединение создаётся при вызовеsend(). -
attachments: Список вложений, которые необходимо добавить к сообщению. Это могут быть экземплярыemail.MIMEBase.MIMEBase, или тройки(filename, content, mimetype). -
headers: Словарь дополнительных заголовков, которые нужно добавить к сообщению. Ключи — имя заголовка, значения — значения заголовка. Ответственность вызывающей стороны заключается в обеспечении того, чтобы имена и значения заголовков имели правильный формат для сообщения электронной почты. Соответствующее атрибут —extra_headers. -
cc: Список или кортеж адресов получателей, используемых в заголовке «Cc» при отправке письма. -
reply_to: Список или кортеж адресов получателей, используемых в заголовке «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, исключения, возникшие при отправке сообщения, будут подавлены. Пустой список получателей не вызовет исключения. -
message()строит объектdjango.core.mail.SafeMIMEText(подкласс класса Pythonemail.MIMEText.MIMEText) или объектdjango.core.mail.SafeMIMEMultipart, содержащий сообщение, которое будет отправлено. Если вам когда-либо понадобится расширить классEmailMessage, вы, вероятно, захотите переопределить этот метод, чтобы поместить нужное содержимое в объект MIME. -
recipients()возвращает список всех получателей сообщения, независимо от того, хранятся ли они в атрибутахto,ccилиbcc. Это ещё один метод, который может потребоваться переопределить при наследовании, так как SMTP-серверу необходимо знать полный список получателей при отправке сообщения. Если вы добавите ещё один способ указания получателей в свой класс, они также должны быть возвращены этим методом. -
attach()создаёт новое вложение файла и добавляет его к сообщению. Существует два способа вызоваattach():- Вы можете передать ему единственный аргумент, который является экземпляром
email.MIMEBase.MIMEBase. Он будет вставлен непосредственно в результирующее сообщение. -
В качестве альтернативы, вы можете передать
attach()три аргумента:filename,contentиmimetype.filename— имя вложения файла, которое будет отображаться в письме;content— данные, которые будут содержаться внутри вложения;mimetype— необязательный тип MIME для вложения. Если вы опуститеmimetype, тип содержимого MIME будет угадан из имени файла вложения.Например:
message.attach('design.png', img_data, 'image/png')Если вы зададите
mimetypeравнымmessage/rfc822, он также будет приниматьdjango.core.mail.EmailMessageиemail.message.Message.Кроме того, вложения
message/rfc822больше не будут кодироваться в base64 в нарушение RFC 2046#section-5.2.1, что может вызвать проблемы с отображением вложений в Evolution и Thunderbird.
- Вы можете передать ему единственный аргумент, который является экземпляром
-
attach_file()создаёт новое вложение, используя файл из вашей файловой системы. Вызовите его с путём к файлу, который нужно добавить, и, необязательно, с типом MIME для этого файла. Если тип MIME опущен, он будет угадан из имени файла. Самое простое использование будет следующим:message.attach_file('/images/weather_map.png')
Отправка альтернативных типов содержимого
Полезно включать несколько версий содержимого в письмо; классический пример — отправка текстовой и 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)[source]
По умолчанию вызов 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был добавлен. Если не указано, значение по умолчаниюtimeoutбудет предоставленоsocket.getdefaulttimeout(), которое по умолчанию равноNone(нет таймаута).Параметры
ssl_keyfile, иssl_certfileи соответствующие настройки были добавлены. Возможность настроитьtimeoutс помощью настройки (EMAIL_TIMEOUT) была добавлена. -
Бэкенд консоли
Вместо отправки реальных электронных писем бэкенд консоли просто записывает электронные письма, которые должны быть отправлены в стандартный вывод. По умолчанию бэкенд консоли записывает в 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'
Этот бэкенд не предназначен для использования в рабочей среде — он предоставляется для удобства использования во время разработки и тестирования.
Бэкенд-заглушка
Как следует из названия, бэкенд-заглушка ничего не делает с вашими сообщениями. Чтобы указать этот бэкенд, поместите следующее в свои настройки:
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-сервер, который получает электронные письма локально и отображает их в терминале, но фактически ничего не отправляет. Python имеет встроенный способ сделать это одной командой:
python -m smtpd -n -c DebuggingServer localhost:1025
Эта команда запустит простой SMTP-сервер, прослушивающий порт 1025 локального хоста. Этот сервер просто выводит в стандартный вывод все заголовки электронных писем и тело электронного письма. Вам нужно только соответствующим образом установить EMAIL_HOST и EMAIL_PORT. Более подробное описание вариантов SMTP-сервера см. в документации Python по модулю smtpd.
Дополнительную информацию о тестировании отправки электронных писем в вашем приложении см. в разделе «Сервисы электронной почты» документации по тестированию.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.8/topics/email/