Spec-Zone.ru › Django 3.2

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

Хотя 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.

Не все возможности класса 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')
      

      Если вы укажете тип 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

Этот бэкенд не предназначен для использования в рабочей среде — он предоставляется как удобство, которое можно использовать во время разработки.

Изменено в Django 3.1:

Добавлена поддержка pathlib.Path.

Бэкенд памяти

Бэкенд '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 вообще отправлял письма. Например, во время разработки веб-сайта, вы, вероятно, не хотите отправлять тысячи писем — но вы можете захотеть проверить, что письма будут отправлены нужным людям при соответствующих условиях и что эти письма будут содержать правильное содержимое.

Самый простой способ настроить электронную почту для разработки — использовать консольный бэкенд для отправки писем. Этот бэкенд перенаправляет все письма на стандартный вывод, что позволяет вам просматривать содержимое писем.

Бэкенд файлов для отправки писем также может быть полезен во время разработки — этот бэкенд выводит содержимое каждого SMTP-соединения в файл, который можно просмотреть по мере необходимости.

Другой подход — использовать «глупый» SMTP-сервер, который получает письма локально и отображает их в терминале, но на самом деле ничего не отправляет. Python предоставляет встроенный способ сделать это одной командой:

python -m smtpd -n -c DebuggingServer localhost:1025

Эта команда запустит минимальный SMTP-сервер, который прослушивает порт 1025 хоста localhost. Этот сервер выводит в стандартный вывод все заголовки писем и тело письма. Вам нужно только соответствующим образом установить EMAIL_HOST и EMAIL_PORT. Более подробное обсуждение опций SMTP-серверов см. в документации Python по модулю smtpd.

Сведения о тестировании отправки писем в вашем приложении см. в разделе «Услуги по отправке электронных писем» документации по тестированию.

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/3.2/topics/email/

Spec-Zone.ru

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