Sending Email in Django: A Complete Guide to send_mail, EmailMessage, and EmailMultiAlternatives

This article provides a comprehensive guide to sending email in Django. It covers the send_mail() helper, mass emailing with send_mass_mail(), advanced email composition using EmailMessage and EmailMultiAlternatives, SMTP configuration, and best practices for secure and reliable email delivery.

Django send_mail, EmailMessageEmailMultiAlternatives, send_mass_mailSMTP settings, Django email backend

~9 min read • Updated Mar 15, 2026

Introduction

Django offers a clean and convenient API for sending email, built on top of Python’s built‑in smtplib. These wrappers simplify email delivery, improve testing during development, and support environments where SMTP is not available.

All email utilities live in the django.core.mail module.


Quick Email Sending with send_mail()

The simplest way to send email in Django is using send_mail(). This function is ideal for sending plain text messages quickly.


from django.core.mail import send_mail

send_mail(
    "Subject here",
    "Here is the message.",
    "[email protected]",
    ["[email protected]"],
    fail_silently=False,
)

Required Parameters

  • subject: Email subject (string)
  • message: Plain text body
  • from_email: Sender address
  • recipient_list: List of recipients

Optional Keyword Arguments

  • fail_silently: Suppress SMTP errors
  • auth_user: Override SMTP username
  • auth_password: Override SMTP password
  • connection: Custom email backend
  • html_message: HTML version of the email

The return value is the number of successfully delivered messages (0 or 1).


Advanced Emails with EmailMessage and EmailMultiAlternatives

For more control—attachments, custom headers, HTML content—use EmailMessage or EmailMultiAlternatives.

Sending Multipart Emails (HTML + Text)


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

text_content = render_to_string(
    "templates/emails/my_email.txt",
    {"my_variable": 42},
)

html_content = render_to_string(
    "templates/emails/my_email.html",
    {"my_variable": 42},
)

msg = EmailMultiAlternatives(
    subject="Subject here",
    body=text_content,
    from_email="[email protected]",
    to=["[email protected]"],
    headers={"List-Unsubscribe": ""},
)

msg.attach_alternative(html_content, "text/html")
msg.send()

This approach is ideal for newsletters, transactional emails, and template‑based messages.


SMTP Configuration

Django sends email using the following settings:

  • EMAIL_HOST: SMTP server hostname
  • EMAIL_PORT: SMTP port
  • EMAIL_HOST_USER: Username
  • EMAIL_HOST_PASSWORD: Password
  • EMAIL_USE_TLS: Enable TLS
  • EMAIL_USE_SSL: Enable SSL

The character set defaults to DEFAULT_CHARSET.


Mass Emailing with send_mass_mail()

Use send_mass_mail() to send multiple emails efficiently using a single SMTP connection.

Example


message1 = (
    "Subject here",
    "Here is the message",
    "[email protected]",
    ["[email protected]", "[email protected]"],
)

message2 = (
    "Another Subject",
    "Here is another message",
    "[email protected]",
    ["[email protected]"],
)

send_mass_mail((message1, message2), fail_silently=False)

Each tuple becomes a separate email message. The return value is the number of successfully delivered messages.


Best Practices

  • Use EmailMultiAlternatives for HTML emails.
  • Use send_mass_mail() for bulk sending.
  • Store SMTP credentials securely (environment variables).
  • Use console or file email backends during development.
  • Always set fail_silently=False in production to detect errors.

Conclusion

Django’s email utilities provide a powerful yet simple interface for sending everything from basic notifications to rich HTML emails. With tools like send_mail(), EmailMessage, and EmailMultiAlternatives, you can build reliable, flexible, and secure email workflows for any application.

send_mail() vs send_mass_mail()

The key difference between send_mail() and send_mass_mail() is how they handle SMTP connections:

  • send_mail() opens a new SMTP connection for every email.
  • send_mass_mail() opens a single connection and sends all messages through it.

This makes send_mass_mail() more efficient when sending multiple emails.


mail_admins()

The mail_admins() function sends an email to all site administrators defined in the ADMINS setting.

Features

  • Automatically prefixes the subject with EMAIL_SUBJECT_PREFIX (default: “[Django] ”).
  • Uses SERVER_EMAIL as the “From:” address.
  • Supports html_message for sending HTML emails.

This function exists for convenience and readability.


mail_managers()

mail_managers() works exactly like mail_admins(), but sends email to the addresses listed in the MANAGERS setting.


Examples

Sending one email to multiple recipients (all appear in “To:”)


send_mail(
    "Subject",
    "Message.",
    "[email protected]",
    ["[email protected]", "[email protected]"],
)

Sending separate emails to each recipient


datatuple = (
    ("Subject", "Message.", "[email protected]", ["[email protected]"]),
    ("Subject", "Message.", "[email protected]", ["[email protected]"]),
)
send_mass_mail(datatuple)

Preventing Header Injection

Header injection is a security attack where an attacker inserts newline characters into email headers to manipulate “To:” or “From:” fields.

Django protects against this by raising ValueError if any header value contains a newline.

Safe Example


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, ["[email protected]"])
        except ValueError:
            return HttpResponse("Invalid header found.")
        return HttpResponseRedirect("/contact/thanks/")
    return HttpResponse("Make sure all fields are entered and valid.")

The EmailMessage Class

The send_mail() and send_mass_mail() functions are thin wrappers around the EmailMessage class.

To use advanced features such as:

  • BCC recipients
  • CC recipients
  • Reply-To headers
  • File attachments
  • Multipart emails

you must create EmailMessage objects directly.

Example


from django.core.mail import EmailMessage

email = EmailMessage(
    subject="Hello",
    body="Body goes here",
    from_email="[email protected]",
    to=["[email protected]", "[email protected]"],
    bcc=["[email protected]"],
    reply_to=["[email protected]"],
    headers={"Message-ID": "foo"},
)

To send the email:


email.send()

Conclusion

Django provides a flexible and powerful email system. Use send_mail() for simple messages, send_mass_mail() for efficient bulk sending, and EmailMessage for full control over email structure. Functions like mail_admins() and mail_managers() make system notifications easy and secure.

Introduction

Django’s email framework goes far beyond simple send_mail() usage. For advanced email composition—attachments, inline images, HTML alternatives, custom headers, and backend control—Django provides the powerful EmailMessage and EmailMultiAlternatives classes.


EmailMessage Methods

send(fail_silently=False)

Sends the email. If a connection was provided, it will be reused; otherwise, Django creates a new backend connection. Returns:

  • 1 → message sent successfully
  • 0 → message not sent

If fail_silently=True, exceptions are suppressed.


message(policy=email.policy.default)

Builds and returns a Python email.message.EmailMessage object. The policy argument controls serialization rules (e.g., SMTP line endings).

Override this method when subclassing EmailMessage to customize the underlying MIME structure.


recipients()

Returns a combined list of all recipients from:

  • to
  • cc
  • bcc

If you add custom recipient fields in a subclass, override this method.


Handling Attachments

attach(filename, content, mimetype)

Two usage patterns:

1. Standard attachment


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

2. MIMEPart attachment (Django 6.0+)

Useful for inline images or attachments requiring custom headers.


import email.utils
from email.message import MIMEPart

cid = email.utils.make_msgid()
inline_image = MIMEPart()
inline_image.set_content(
    image_data_bytes,
    maintype="image",
    subtype="png",
    disposition="inline",
    cid=cid,
)
message.attach(inline_image)
message.attach_alternative(f'', "text/html")

Support for legacy MIMEBase is deprecated.


attach_file(path, mimetype=None)

Attach a file directly from disk:


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

EmailAttachment (Django 5.2+)

A named tuple with:

  • filename
  • content
  • mimetype

Used internally to store attachments.


Sending Multiple Content Types

EmailMultiAlternatives

A subclass of EmailMessage that supports multiple body formats (e.g., text + HTML).

Example


from django.core.mail import EmailMultiAlternatives

msg = EmailMultiAlternatives(
    "hello",
    "This is an important message.",
    "[email protected]",
    ["[email protected]"],
)
msg.attach_alternative("

This is an important message.

", "text/html") msg.send()

alternatives attribute

A list of EmailAlternative named tuples (content, mimetype).


body_contains(text)

Checks whether the given text appears in:

  • the main body
  • all text/* alternatives

Useful for testing.


Changing the Default Content Type

By default, EmailMessage uses text/plain. To send HTML as the main body:


msg = EmailMessage(subject, html_content, from_email, [to])
msg.content_subtype = "html"
msg.send()

Email Backends

The backend handles the actual sending of messages.

Backend Methods

  • open() → opens a long-lived connection
  • close() → closes the connection
  • send_messages() → sends a list of EmailMessage objects

Using a backend as a context manager


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

This ensures efficient reuse of the same SMTP connection.


Conclusion

Django’s advanced email system provides everything needed for professional-grade email delivery: attachments, inline images, HTML alternatives, custom headers, and backend control. With EmailMessage, EmailMultiAlternatives, and flexible backends, you can build robust and scalable email workflows for any application.

Obtaining an Email Backend Instance

The get_connection() function in django.core.mail returns an instance of the email backend you want to use.


from django.core.mail import get_connection

connection = get_connection()

Parameters

  • backend: Optional import path to override EMAIL_BACKEND
  • fail_silently: If True, suppresses exceptions during sending
  • Other keyword arguments → passed directly to the backend constructor

If no backend is specified, Django uses the EMAIL_BACKEND setting.


Built‑in Django Email Backends

1. SMTP Backend (Default)

Sends real emails through an SMTP server.

Settings used:

  • EMAIL_HOST
  • EMAIL_PORT
  • EMAIL_HOST_USER
  • EMAIL_HOST_PASSWORD
  • EMAIL_USE_TLS
  • EMAIL_USE_SSL
  • EMAIL_TIMEOUT
  • EMAIL_SSL_KEYFILE
  • EMAIL_SSL_CERTFILE

Explicit configuration:


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

2. Console Backend

Writes emails to stdout instead of sending them. Ideal for development.


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

3. File Backend

Writes each email to a file on disk.


EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"
EMAIL_FILE_PATH = "/tmp/app-messages"

4. In‑Memory Backend (locmem)

Stores emails in django.core.mail.outbox. Perfect for testing.


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

5. Dummy Backend

Does nothing—emails are ignored. Useful for development.


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

Creating a Custom Email Backend

If you need custom email‑sending behavior, you can write your own backend.

Custom backends must:

  • Subclass BaseEmailBackend
  • Implement send_messages(email_messages)

If your backend maintains a persistent connection, also implement:

  • open()
  • close()

The smtp.EmailBackend class is a good reference implementation.


Sending Multiple Emails Efficiently

Opening and closing SMTP connections is expensive. Django provides two ways to reuse a connection.

Method 1: Using send_messages()

Send a list of messages using a single connection.


from django.core import mail

connection = mail.get_connection()
messages = get_notification_email()
connection.send_messages(messages)

Method 2: Manually opening and closing the connection


from django.core import mail

connection = mail.get_connection()
connection.open()

email1 = mail.EmailMessage("Hello", "Body", "[email protected]", ["to1"], connection=connection)
email1.send()

email2 = mail.EmailMessage("Hello", "Body", "[email protected]", ["to2"])
email3 = mail.EmailMessage("Hello", "Body", "[email protected]", ["to3"])

connection.send_messages([email2, email3])
connection.close()

Email Configuration for Development

During development, you often want to inspect emails without actually sending them.

Recommended options

  • Console backend → prints emails to terminal
  • File backend → writes emails to disk
  • LocMem backend → stores emails in memory for tests

Using a local SMTP server with aiosmtpd


python -m pip install "aiosmtpd >= 1.4.5"
python -m aiosmtpd -n -l localhost:8025

This server prints all email headers and bodies to stdout. Just set EMAIL_HOST and EMAIL_PORT accordingly.


Conclusion

Django’s email backend system provides flexibility for every scenario—from real SMTP delivery to development‑friendly console and file backends. With get_connection(), custom backend support, and efficient multi‑email sending, Django gives you full control over how emails are delivered in your application.

Written & researched by Dr. Shahin Siami