Envío de email en Django

Básico
Django
Django
Actualizado: 18/04/2026

Configuración del backend de email

# settings.py

# Desarrollo: imprime en consola (no envía emails reales)
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

# Desarrollo: guarda emails como archivos
EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
EMAIL_FILE_PATH = BASE_DIR / 'emails'

# Producción con SMTP (Gmail como ejemplo)
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD')
DEFAULT_FROM_EMAIL = 'CertiTienda <noreply@certitienda.com>'
SERVER_EMAIL = 'errors@certitienda.com'  # Para errores del servidor

Diagrama conceptual de Envío de email en Django

send_mail() para emails simples

from django.core.mail import send_mail, send_mass_mail

# Email de texto plano
send_mail(
    subject='Bienvenido a CertiTienda',
    message='Gracias por registrarte. Tu cuenta ha sido creada correctamente.',
    from_email='noreply@certitienda.com',
    recipient_list=['usuario@ejemplo.com'],
    fail_silently=False,  # Lanzar excepción si falla
)

# Envío masivo eficiente (una sola conexión SMTP)
mensajes = [
    ('Confirmación de pedido #001', 'Tu pedido ha sido confirmado.', None, ['cliente1@ejemplo.com']),
    ('Confirmación de pedido #002', 'Tu pedido ha sido confirmado.', None, ['cliente2@ejemplo.com']),
]
send_mass_mail(mensajes, fail_silently=False)

EmailMessage para emails avanzados

from django.core.mail import EmailMessage, EmailMultiAlternatives

# Email con adjunto
def enviar_factura(pedido):
    email = EmailMessage(
        subject=f'Factura del pedido #{pedido.id}',
        body='Adjuntamos la factura de tu pedido reciente.',
        from_email='facturacion@certitienda.com',
        to=[pedido.cliente.email],
        cc=['contabilidad@certitienda.com'],
        reply_to=['soporte@certitienda.com'],
    )
    # Añadir adjunto desde archivo
    with open(f'/facturas/pedido_{pedido.id}.pdf', 'rb') as f:
        email.attach(f'factura_{pedido.id}.pdf', f.read(), 'application/pdf')

    email.send(fail_silently=False)

Email HTML con alternativa de texto

from django.template.loader import render_to_string
from django.utils.html import strip_tags

def enviar_bienvenida(usuario):
    """Envía email de bienvenida con versión HTML y texto plano."""
    contexto = {
        'nombre': usuario.get_full_name() or usuario.username,
        'username': usuario.username,
        'url_activacion': f'https://certitienda.com/activar/{usuario.profile.token}',
    }

    # Renderizar plantillas
    html_content = render_to_string('emails/bienvenida.html', contexto)
    text_content = strip_tags(html_content)  # Versión texto plano automática

    email = EmailMultiAlternatives(
        subject='Bienvenido a CertiTienda 🎉',
        body=text_content,           # Versión texto plano
        from_email=None,             # Usa DEFAULT_FROM_EMAIL
        to=[usuario.email],
    )
    email.attach_alternative(html_content, 'text/html')  # Versión HTML
    email.send()

# Plantilla HTML del email
# templates/emails/bienvenida.html
<!-- templates/emails/bienvenida.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <style>
        body { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; }
        .header { background: #0b4b33; color: white; padding: 20px; }
        .content { padding: 20px; }
        .button { background: #0b4b33; color: white; padding: 12px 24px; text-decoration: none; }
    </style>
</head>
<body>
    <div class="header">
        <h1>Bienvenido a CertiTienda</h1>
    </div>
    <div class="content">
        <p>Hola {{ nombre }},</p>
        <p>Tu cuenta con el usuario <strong>{{ username }}</strong> ha sido creada correctamente.</p>
        <p>Por favor, activa tu cuenta haciendo clic en el siguiente enlace:</p>
        <a class="button" href="{{ url_activacion }}">Activar cuenta</a>
        <p>Si no solicitaste este registro, ignora este correo.</p>
    </div>
</body>
</html>

Envío asíncrono con Celery

Para no bloquear la respuesta de la vista mientras se envía el email:

# tasks.py (Celery)
from celery import shared_task
from django.core.mail import send_mail

@shared_task(bind=True, max_retries=3)
def enviar_email_async(self, asunto, mensaje, destinatarios):
    try:
        send_mail(asunto, mensaje, None, destinatarios)
    except Exception as exc:
        raise self.retry(exc=exc, countdown=60)

# En la vista
def registro(request):
    # ... guardar usuario ...
    enviar_email_async.delay(
        'Bienvenido',
        f'Hola {usuario.username}!',
        [usuario.email]
    )
    return redirect('dashboard')
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Django es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Django

Explora más contenido relacionado con Django y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Configurar el backend de email en settings.py para desarrollo y producción. Enviar emails simples con send_mail() y send_mass_mail(). Usar EmailMessage para emails con archivos adjuntos y cabeceras personalizadas. Crear plantillas HTML de email con Django Template Language. Enviar emails con contenido HTML y texto plano como alternativa.