Spec-Zone.ru › Django 4.0

Отправка электронных писем

Хотя 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 предоставлено, результирующее письмо будет иметь формат 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 предоставлено, результирующее письмо будет иметь формат 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.

END_OF_DOCUMENT_MARKER

Не все возможности класса EmailMessage доступны через функции send_mail() и связанные с ними функции-обертки. Если вы хотите использовать расширенные возможности, такие как адресаты с кодированием BCC, вложения файлов или многочастные электронные письма, вам нужно создать экземпляры EmailMessage непосредственно.

Примечание

Это особенность дизайна. Функции send_mail() и связанные с ними функции изначально были единственным интерфейсом, предоставляемым Django. Однако список параметров, которые они принимали, постепенно рос со временем. Это было логично перейти к более объектно-ориентированному дизайну электронных писем и сохранить исходные функции только для обратной совместимости.

Класс EmailMessage отвечает за создание самого электронного письма. Затем почтовый бэкенд отвечает за отправку письма.

Для удобства 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 MIMEText) или объект 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')
      

      Если вы укажете mimetype типа message/rfc822, он также будет принимать django.core.mail.EmailMessage и email.message.Message.

      Для mimetype, начинающегося с 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-сервер, который получает письма локально и отображает их в терминале, но фактически ничего не отправляет. 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/4.0/topics/email/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API