Enviando correo electrónico

Aunque Python proporciona una interfaz para enviar correos electrónicos mediante el módulo smtplib, Django ofrece un par de envolturas ligeras sobre él. Estas envolturas se proporcionan para hacer que enviar correos electrónicos sea extra rápido, ayudar a probar la envío de correos electrónicos durante el desarrollo y proporcionar soporte para plataformas que no pueden utilizar SMTP.

El código se encuentra en el módulo django.core.mail.

Ejemplos rápidos

Usa la función send_mail() para enviar correos electrónicos de manera directa. Por ejemplo, para enviar un mensaje de texto plano:

from django.core.mail import send_mail

send_mail(
    "Subject here",
    "Here is the message.",
    "from@example.com",
    ["to@example.com"],
    fail_silently=False,
)

When se necesita funcionalidad adicional para enviar correos electrónicos, utilice EmailMessage o EmailMultiAlternatives. Por ejemplo, para enviar un correo electrónico multipart que incluya tanto versiones HTML como de texto plano con un plantilla específica y encabezados personalizados, puede utilizar la siguiente aproximación:

from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string

# First, render the plain text content.
text_content = render_to_string(
    "templates/emails/my_email.txt",
    context={"my_variable": 42},
)

# Secondly, render the HTML content.
html_content = render_to_string(
    "templates/emails/my_email.html",
    context={"my_variable": 42},
)

# Then, create a multipart email instance.
msg = EmailMultiAlternatives(
    "Subject here",
    text_content,
    "from@example.com",
    ["to@example.com"],
    headers={"List-Unsubscribe": "<mailto:unsub@example.com>"},
)

# Lastly, attach the HTML content to the email instance and send.
msg.attach_alternative(html_content, "text/html")
msg.send()

El envío de correos electrónicos se realiza utilizando el host SMTP y puerto especificado en las configuraciones EMAIL_HOST y EMAIL_PORT. Las configuraciones EMAIL_HOST_USER y EMAIL_HOST_PASSWORD, si están establecidas, se utilizan para autenticarse con el servidor SMTP, y las configuraciones EMAIL_USE_TLS y EMAIL_USE_SSL controlan si se utiliza una conexión segura.

Nota

El conjunto de caracteres del correo electrónico enviado con django.core.mail se establecerá en el valor de la configuración 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)[fuente]

En la mayoría de los casos, puede enviar correos electrónicos utilizando django.core.mail.send_mail().

Los parámetros subject, message, from_email y recipient_list son obligatorios.

  • subject: Una cadena de caracteres.

  • message: Una cadena de caracteres.

  • from_email: Una cadena de caracteres. Si es None, Django utilizará el valor de la configuración DEFAULT_FROM_EMAIL.

  • recipient_list: Una lista de cadenas, cada una un dirección de correo electrónico. Cada miembro de recipient_list verá a los demás destinatarios en el campo «To:» del mensaje de correo electrónico.

  • fail_silently: Un booleano. Cuando es False, send_mail() lanzará una excepción de tipo smtplib.SMTPException si ocurre un error. Consulte la documentación del módulo smtplib para obtener una lista de posibles excepciones, todas las cuales son subclases de SMTPException.

  • auth_user: El nombre de usuario opcional a utilizar para autenticarse en el servidor SMTP. Si no se proporciona este valor, Django utilizará el valor del parámetro de configuración EMAIL_HOST_USER.

  • auth_password: La contraseña opcional a utilizar para autenticarse en el servidor SMTP. Si no se proporciona este valor, Django utilizará el valor del parámetro de configuración EMAIL_HOST_PASSWORD.

  • connection: El backend de correo electrónico opcional a utilizar para enviar el correo electrónico. Si no se especifica, se utilizará una instancia del backend predeterminado. Consulte la documentación sobre backends de correo electrónico en <topic-email-backends> para obtener más detalles.

  • Si se proporciona html_message, el correo electrónico resultante será un correo electrónico multipart/alternative con message como tipo de contenido text/plain y html_message como tipo de contenido text/html.

El valor devuelto será el número de correos electrónicos entregados correctamente (que puede ser 0 o 1 ya que solo se envía un mensaje).

send_mass_mail()

send_mass_mail(datatuple, fail_silently=False, auth_user=None, auth_password=None, connection=None)[fuente]

django.core.mail.send_mass_mail() está diseñado para manejar la envío masivo de correos electrónicos.

datatuple es una tupla en la que cada elemento tiene este formato:

(subject, message, from_email, recipient_list)

fail_silently, auth_user y auth_password tienen las mismas funciones que en send_mail().

Cada elemento separado de datatuple da lugar a un mensaje de correo electrónico separado. Al igual que en send_mail(), los destinatarios en la misma lista recipient_list verán todos los demás direcciones en el campo «To:» del mensaje de correo electrónico.

Por ejemplo, el siguiente código enviaría dos mensajes diferentes a dos conjuntos de destinatarios diferentes; sin embargo, solo se abriría una conexión al servidor de correo electrónico:

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)

El valor de retorno será el número de mensajes entregados con éxito.

send_mass_mail() vs. send_mail()

La principal diferencia entre send_mass_mail() y send_mail() es que send_mail() abre una conexión al servidor de correo electrónico cada vez que se ejecuta, mientras que send_mass_mail() utiliza una sola conexión para todos sus mensajes. Esto hace que send_mass_mail() sea ligeramente más eficiente.

mail_admins()

mail_admins(subject, message, fail_silently=False, connection=None, html_message=None)[fuente]

mail_admins() es un atajo para enviar un correo electrónico a los administradores del sitio, tal como se define en la configuración ADMINS.

mail_admins() agrega el prefijo de asunto con el valor de la configuración EMAIL_SUBJECT_PREFIX, que es "[Django] " por defecto.

El encabezado «From:» del correo electrónico será el valor de la configuración SERVER_EMAIL.

Este método existe para conveniencia y legibilidad.

Si html_message se proporciona, el correo electrónico resultante será un correo electrónico multipart/alternative con message como tipo de contenido text/plain y html_message como tipo de contenido text/html.

mail_managers()

mail_managers(subject, message, fail_silently=False, connection=None, html_message=None)[fuente]

django.core.mail.mail_managers() es igual que mail_admins(), excepto que envía un correo electrónico a los administradores del sitio, tal y como se define en la configuración MANAGERS.

Ejemplos

Esto envía un solo correo electrónico a john@example.com y jane@example.com, con ambos apareciendo en el «To:».

send_mail(
    "Subject",
    "Message.",
    "from@example.com",
    ["john@example.com", "jane@example.com"],
)

Esto envía un mensaje a john@example.com y jane@example.com, con ambos recibiendo un correo electrónico separado.

datatuple = (
    ("Subject", "Message.", "from@example.com", ["john@example.com"]),
    ("Subject", "Message.", "from@example.com", ["jane@example.com"]),
)
send_mass_mail(datatuple)

Prevención de inyección de encabezados

La inyección de encabezados es una vulnerabilidad de seguridad en la que un atacante inserta encabezados adicionales para controlar el «To:» y el «From:» en los mensajes de correo electrónico generados por tus scripts.

Las funciones de correo electrónico de Django descritas anteriormente protegen contra la inyección de encabezados prohibiendo nuevas líneas en los valores de encabezado. Si subject, from_email o recipient_list contiene una nueva línea (en estilo Unix, Windows o Mac), la función de correo electrónico (por ejemplo, send_mail()) levantará un error BadHeaderError (una subclase de ValueError) y, por lo tanto, no enviará el correo electrónico. Es tu responsabilidad validar todos los datos antes de pasarlos a las funciones de correo electrónico.

Si message contiene encabezados al principio de la cadena, se imprimirán como la primera parte del mensaje de correo electrónico.

Aquí tienes un ejemplo de vista que toma los parámetros subject, message y from_email desde los datos POST de la solicitud, envía ese contenido a admin@example.com y redirige a «/contact/thanks/» cuando está listo:

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.")

La clase EmailMessage

Las funciones de Django send_mail y send_mass_mail <func>~django.core.mail.send_mail</func> y <func>~django.core.mail.send_mass_mail</func> son en realidad envolturas delgadas que utilizan la clase <class>~django.core.mail.EmailMessage</class>.

No todas las características de la clase EmailMessage están disponibles a través de las funciones de envoltorio relacionadas con send_mail(). Si deseas utilizar características avanzadas, como destinatarios BCC, adjuntos de archivo o correo electrónico en formato multi-partido, necesitarás crear instancias de la clase EmailMessage directamente.

Nota

Esta es una característica de diseño. send_mail() y funciones relacionadas fueron originalmente la única interfaz que proporcionaba Django. Sin embargo, la lista de parámetros que aceptaban fue creciendo lentamente con el tiempo. Hacía sentido pasar a un diseño más orientado a objetos para los mensajes de correo electrónico y mantener las funciones originales solo por compatibilidad hacia atrás.

EmailMessage es responsable de crear el mensaje de correo electrónico en sí mismo. El backend de correo electrónico es luego responsable de enviar el correo electrónico.

Para conveniencia, la clase ~django.core.mail.EmailMessage proporciona un método send() para enviar un correo electrónico individual. Si necesitas enviar múltiples mensajes, la API del backend de correos electrónicos proporciona una alternativa.

Objetos EmailMessage

class EmailMessage[fuente]

La clase EmailMessage se inicializa con los siguientes parámetros (en el orden dado, si se utilizan argumentos posicionales). Todos los parámetros son opcionales y pueden establecerse en cualquier momento antes de llamar al método send().

  • subject: La línea del asunto del correo electrónico.

  • body: El texto del cuerpo. Este debe ser un mensaje de texto plano.

  • from_email: La dirección del remitente. Ambas formas fred@example.com y "Fred" <fred@example.com> son legales. Si se omite, se utiliza el parámetro DEFAULT_FROM_EMAIL.

  • to: Una lista o tupla de direcciones de destinatarios.

  • bcc: Una lista o tupla de direcciones utilizadas en la cabecera «Bcc» al enviar el correo electrónico.

  • connection: Instancia de un backend de correo electrónico. Utilice este parámetro si está enviando el EmailMessage mediante send() y quiere utilizar la misma conexión para múltiples mensajes. Si se omite, se crea una nueva conexión cuando se llama a send(). Este parámetro se ignora al usar send_messages().

  • attachments: Una lista de adjuntos para colocar en el mensaje. Estos pueden ser instancias de MIMEBase o EmailAttachment, o una tupla con atributos (nombre del archivo, contenido, mimetype).

    Se agregó soporte para elementos EmailAttachment de la clase ~django.core.mail.EmailAttachment`` en attachments.

  • headers: Un diccionario de encabezados adicionales para colocar en el mensaje. Las claves son los nombres de encabezado, y los valores son los valores de encabezado. Es responsabilidad del llamador asegurarse de que los nombres y valores de encabezado estén en el formato correcto para un mensaje de correo electrónico. La correspondiente propiedad es extra_headers.

  • cc: Una lista o tupla de direcciones de destinatarios utilizadas en la cabecera «Cc» al enviar el correo electrónico.

  • reply_to: Una lista o tupla de direcciones de destinatarios utilizadas en la cabecera «Reply-To» al enviar el correo electrónico.

Por ejemplo:

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"},
)

La clase tiene los siguientes métodos:

  • Envía el mensaje con send(fail_silently=False). Si se especificó una conexión cuando se construyó el correo electrónico, esa conexión se utilizará. De lo contrario, se instanciará y se utilizará una instancia del backend predeterminado. Si el argumento de palabra clave fail_silently es True, las excepciones levantadas mientras se envía el mensaje se silencian. Una lista vacía de destinatarios no levanta una excepción. Devuelve 1 si el mensaje se envió con éxito, en caso contrario devuelve 0.

  • message() construye un objeto django.core.mail.SafeMIMEText (una subclase de la clase MIMEText de Python) o un objeto django.core.mail.SafeMIMEMultipart que contiene el mensaje a enviar. Si alguna vez necesitas extender la clase EmailMessage, probablemente querrás sobreescribir este método para poner el contenido que deseas en el objeto MIME.

  • recipients() devuelve una lista de todos los destinatarios del mensaje, ya sean registrados en las propiedades to, cc o bcc. Este es otro método que podrías necesitar sobrescribir al heredar, porque el servidor SMTP necesita saber la lista completa de destinatarios cuando se envía el mensaje. Si agregas otra forma de especificar destinatarios en tu clase, deben ser devueltos también por este método.

  • Crea un nuevo archivo adjunto y lo agrega al mensaje. Hay dos formas de llamar a attach():

    • Puedes pasarle un solo argumento que sea una instancia de MIMEBase. Este se insertará directamente en el mensaje resultante.

    • Alternativamente, puedes pasar a attach() tres argumentos: filename, content y mimetype. filename es el nombre del archivo adjunto tal como aparecerá en el correo electrónico, content es los datos que se contendrán dentro del archivo adjunto y mimetype es el tipo MIME opcional para el archivo adjunto. Si omites mimetype, el tipo de contenido MIME se adivinará desde el nombre del archivo adjunto.

      Por ejemplo:

      message.attach("design.png", img_data, "image/png")
      

      Si especificas un mimetype de message/rfc822, también aceptará django.core.mail.EmailMessage y email.message.Message.

      Para un mimetype que comienza con text/, se espera que el contenido sea una cadena de texto. Los datos binarios se decodificarán utilizando UTF-8, y si eso falla, el tipo MIME se cambiará a application/octet-stream y los datos se adjuntarán sin cambios.

      Además, las adjuntas de tipo message/rfc822 ya no serán codificadas en base64 en violación de la sección 5.2.1 del RFC 2046 (RFC 2046 Section 5.2.1), lo que puede causar problemas al mostrar las adjuntas en Evolution y Thunderbird.

  • attach_file() crea un nuevo archivo adjunto utilizando un archivo desde tu sistema de archivos. Llámalo con la ruta del archivo a adjuntar y, opcionalmente, el tipo MIME a utilizar para el archivo adjunto. Si se omite el tipo MIME, se adivinará desde el nombre del archivo. Puedes usarlo así:

    message.attach_file("/images/weather_map.png")
    

    Para tipos MIME que comienzan con text/, los datos binarios se manejan como en attach().

class EmailAttachment

Una tupla nombrada para almacenar archivos adjuntos a un correo electrónico.

La tupla nombrada tiene los siguientes índices:

  • filename

  • content

  • mimetype

Enviar tipos de contenido alternativos

Enviar varias versiones del contenido

Puede ser útil incluir varias versiones del contenido en un correo electrónico; el ejemplo clásico es enviar tanto la versión de texto como la HTML de un mensaje. Con la biblioteca de correos de Django, puedes hacer esto utilizando la clase EmailMultiAlternatives.

class EmailMultiAlternatives[fuente]

Una instancia de una clase que hereda de EmailMessage y permite versiones adicionales del cuerpo del mensaje en el correo electrónico a través del método attach_alternative(). Esta herencia directa incluye todos los métodos (incluida la inicialización de la clase) de EmailMessage.

alternatives

Una lista de tuplas nombradas EmailAlternative . Esto es particularmente útil en pruebas:

self.assertEqual(len(msg.alternatives), 1)
self.assertEqual(msg.alternatives[0].content, html_content)
self.assertEqual(msg.alternatives[0].mimetype, "text/html")

Los alternativos deben agregarse solo utilizando el método attach_alternative() o pasados al constructor.

En versiones anteriores, alternatives era una lista de tuplas regulares, en lugar de tuplas nombradas EmailAlternative.

attach_alternative(content, mimetype)[fuente]

Adjuntar una representación alternativa del cuerpo del mensaje en el correo electrónico.

Por ejemplo, para enviar un conjunto de texto y HTML, podrías escribir:

from django.core.mail import EmailMultiAlternatives

subject = "hello"
from_email = "from@example.com"
to = "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()
body_contains(text)[fuente]

Devuelve un booleano indicando si el text proporcionado está contenido en el cuerpo del correo electrónico body y en todas las alternativas MIME tipo text/* adjuntas.

Esto puede ser útil cuando se están probando correos electrónicos. Por ejemplo:

def test_contains_email_content(self):
    subject = "Hello World"
    from_email = "from@example.com"
    to = "to@example.com"
    msg = EmailMultiAlternatives(subject, "I am content.", from_email, [to])
    msg.attach_alternative("<p>I am content.</p>", "text/html")

    self.assertIs(msg.body_contains("I am content"), True)
    self.assertIs(msg.body_contains("<p>I am content.</p>"), False)
class EmailAlternative

Una tupla nombrada para almacenar versiones alternativas de contenido de correo electrónico.

La tupla nombrada tiene los siguientes índices:

  • content

  • mimetype

Actualización del tipo de contenido predeterminado

By defecto, el tipo MIME del parámetro body en un mensaje de correo electrónico EmailMessage es "text/plain". Es buena práctica dejarlo así, ya que garantiza que cualquier destinatario podrá leer el correo electrónico, independientemente de su cliente de correo electrónico. Sin embargo, si estás seguro de que tus destinatarios pueden manejar un tipo de contenido alternativo, puedes utilizar la propiedad content_subtype en la clase EmailMessage para cambiar el tipo principal de contenido. El tipo mayor siempre será "text", pero puedes cambiar el subtipo.

msg = EmailMessage(subject, html_content, from_email, [to])
msg.content_subtype = "html"  # Main content is now text/html
msg.send()

Los backends de correo electrónico

La envío real de un correo electrónico es gestionado por el backend de correo electrónico.

La clase del backend de correo electrónico tiene los siguientes métodos:

  • open() instancia una conexión para enviar correos electrónicos que dura toda la vida.

  • close() cierra la conexión actual para enviar correos electrónicos.

  • send_messages(email_messages) envía una lista de objetos EmailMessage. Si la conexión no está abierta, esta llamada abrirá la conexión automáticamente y la cerrará después. Si la conexión ya está abierta, se dejará abierta después del envío de correo electrónico.

También puede usarse como un administrador de contexto, que llamará open() y close() según sea necesario:

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()

Obteniendo una instancia del backend de correo electrónico

La función get_connection() en django.core.mail devuelve una instancia del backend de correo electrónico que puedes usar.

get_connection(backend=None, fail_silently=False, **kwargs)[fuente]

Por defecto, una llamada a get_connection() devolverá una instancia del backend de correo electrónico especificado en EMAIL_BACKEND. Si especificas el argumento backend, se instanciará una instancia de ese backend.

El argumento fail_silently controla cómo el backend debe manejar los errores. Si fail_silently es Verdadero, las excepciones durante el proceso de envío de correo electrónico serán ignoradas silenciosamente.

Todos los otros argumentos de palabra clave se pasan directamente a la constructor del backend de correo electrónico.

Django viene con varios backends para enviar correos electrónicos. Con la excepción del backend SMTP (que es el predeterminado), estos backends solo son útiles durante la prueba y el desarrollo. Si tienes requisitos especiales para enviar correos electrónicos, puedes escribir tu propio backend de correo electrónico.

Backend 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)

Este es el backend predeterminado. El correo electrónico se enviará a través de un servidor SMTP.

El valor para cada argumento se obtiene del ajuste correspondiente si el argumento es None:

  • host: EMAIL_HOST

  • port: EMAIL_PORT

  • username: EMAIL_HOST_USER

  • La traducción de los textos es la siguiente:

  • use_tls: USAR_TLS

  • use_ssl: USAR_SSL

  • timeout: TIEMPO_LIMITE

  • ssl_keyfile: ARCHIVO_LLAVE_SSL

  • ssl_certfile: ARCHIVO_CERTIFICADO_SSL

La backend SMTP es la configuración por defecto heredada por Django. Si deseas especificarlo explícitamente, coloca lo siguiente en tus ajustes:

EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"

Si no se especifica, el tiempo límite predeterminado será el proporcionado por socket.getdefaulttimeout(), que tiene como valor predeterminado None (sin tiempo límite).

Backend de consola

En lugar de enviar correos electrónicos reales, el backend de consola escribe los correos electrónicos que se enviarían a la salida estándar. Por defecto, el backend de consola escribe en stdout. Puedes utilizar un objeto stream-like diferente proporcionando la argumento clave stream cuando se construye la conexión.

Para especificar este backend, coloca lo siguiente en tus configuraciones:

EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"

Este backend no está destinado a su uso en producción – se proporciona como una comodidad que puede usarse durante el desarrollo.

Backend de archivo

El backend de archivo escribe correos electrónicos a un archivo. Se crea un nuevo archivo para cada nueva sesión que se abre en este backend. El directorio al que se escriben los archivos se toma del EMAIL_FILE_PATH configuración o desde el file_path palabra clave cuando se crea una conexión con get_connection().

Para especificar este backend, coloca lo siguiente en tus configuraciones:

EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"
EMAIL_FILE_PATH = "/tmp/app-messages"  # change this to a proper location

Este backend no está destinado a su uso en producción – se proporciona como una comodidad que puede usarse durante el desarrollo.

Backend de memoria

El 'locmem' backend almacena mensajes en un atributo especial del módulo django.core.mail. El atributo outbox se crea cuando se envía el primer mensaje. Es una lista con un EmailMessage instancia para cada mensaje que se enviaría.

Para especificar este backend, coloca lo siguiente en tus configuraciones:

EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"

Este backend no está destinado a su uso en producción – se proporciona como una comodidad que puede usarse durante el desarrollo y la prueba.

El ejecutor de pruebas de Django usa automáticamente este backend para las pruebas.

Backend dummy

Como sugiere el nombre, el backend dummy no hace nada con tus mensajes. Para especificar este backend, coloca lo siguiente en tus configuraciones:

EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"

Este backend no está destinado a su uso en producción – se proporciona como una comodidad que puede usarse durante el desarrollo.

Definir un back-end de correo electrónico personalizado

Si necesitas cambiar cómo se envían los correos electrónicos puedes escribir tu propio back-end de correo electrónico. La configuración EMAIL_BACKEND en tu archivo de configuración es entonces el camino de importación Python para la clase de tu back-end.

Los back-ends de correo electrónico personalizados deben heredar de BaseEmailBackend que se encuentra en el módulo django.core.mail.backends.base. Un back-end de correo electrónico personalizado debe implementar el método send_messages(email_messages). Este método recibe una lista de instancias de EmailMessage y devuelve el número de mensajes entregados con éxito. Si tu back-end tiene algún concepto de sesión persistente o conexión, también debes implementar los métodos open() y close(). Consulta smtp.EmailBackend para una implementación de referencia.

Enviar múltiples correos electrónicos

Establecer y cerrar una conexión SMTP (o cualquier otra conexión de red, por cierto) es un proceso costoso. Si tienes muchos correos electrónicos que enviar, tiene sentido reutilizar una conexión SMTP en lugar de crear y destruir una conexión cada vez que quieras enviar un correo electrónico.

Hay dos formas de decirle a un back-end de correo electrónico que reutilice una conexión.

Primero, puedes utilizar el método send_messages() en una conexión. Este toma una lista de instancias de EmailMessage (o subclase) y los envía todos utilizando esa sola conexión. Como consecuencia, cualquier conexión establecida en un mensaje individual se ignora.

Por ejemplo, si tienes una función llamada get_notification_email() que devuelve una lista de objetos EmailMessage representando algún correo electrónico periódico que deseas enviar, podrías enviar estos correos electrónicos utilizando un solo llamado a send_messages:

from django.core import mail

connection = mail.get_connection()  # Use default email connection
messages = get_notification_email()
connection.send_messages(messages)

En este ejemplo, la llamada a send_messages() abre una conexión en el back-end, envía la lista de mensajes y luego cierra la conexión nuevamente.

La segunda forma es utilizar los métodos open() y close() en el back-end del correo electrónico para controlar manualmente la conexión. send_messages() no abrirá ni cerrará la conexión manualmente si ya está abierta, por lo que si abres la conexión manualmente puedes controlar cuándo se cierra. Por ejemplo:

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()

Configurando el correo electrónico para desarrollo

Hay momentos en los que no deseas que Django envíe correos electrónicos en absoluto. Por ejemplo, mientras desarrollas un sitio web, probablemente no querrás enviar miles de correos electrónicos – pero podrías querer validar que se enviarán correos electrónicos a las personas correctas bajo las condiciones adecuadas y que esos correos electrónicos contengan el contenido correcto.

La forma más fácil de configurar el correo electrónico para desarrollo local es utilizar la consola backend de correo electrónico. Este backend redirige todos los correos electrónicos a stdout, lo que te permite inspeccionar el contenido del correo.

La archivo backend de correo electrónico también puede ser útil durante el desarrollo – este backend drena los contenidos de cada conexión SMTP en un archivo que puedes inspeccionar con comodidad.

Otra forma es utilizar un servidor SMTP «tonto» que reciba los correos electrónicos localmente y los muestre en la terminal, pero no envíe nada en realidad. El paquete aiosmtpd proporciona una manera de lograr esto:

python -m pip install aiosmtpd

python -m aiosmtpd -n -l localhost:8025

Este comando iniciará un servidor SMTP mínimo que escucha en el puerto 8025 de localhost. Este servidor imprime a salida estándar todos los encabezados del correo electrónico y el cuerpo del correo electrónico. Luego solo necesitas configurar EMAIL_HOST y EMAIL_PORT según corresponda. Para una discusión más detallada de las opciones del servidor SMTP, consulta la documentación del módulo aiosmtpd.

Para obtener información sobre la prueba unitaria de envío de correos electrónicos en tu aplicación, consulta la sección Servicios de correo electrónico de la documentación de pruebas.