Email Composition and Delivery
To send emails in this project, you use the EmailMessage class for composition and the MAILERS configuration to define how those messages are delivered.
Sending a Basic Email
The EmailMessage class is the primary tool for composing plain text emails. It handles headers, recipients, and the message body.
from django.core.mail import EmailMessage
email = EmailMessage(
subject="Order Confirmation",
body="Your order #12345 has been shipped.",
from_email="support@example.com",
to=["customer@example.com"],
cc=["archive@example.com"],
reply_to=["helpdesk@example.com"],
)
email.send()
Key Requirements:
- Recipients: The
to,cc,bcc, andreply_toarguments must be lists or tuples. Passing a single string will raise aTypeError. - From Email: If
from_emailis omitted, the value ofsettings.DEFAULT_FROM_EMAILis used.
Sending HTML and Multipart Emails
To send emails with both plain text and HTML versions, use the EmailMultiAlternatives class. This ensures that clients that cannot render HTML can still display the plain text version.
from django.core.mail import EmailMultiAlternatives
subject = "Weekly Newsletter"
text_content = "Check out our latest updates on the website."
html_content = "<h1>Weekly Newsletter</h1><p>Check out our <strong>latest updates</strong> on the website.</p>"
from_email = "newsletter@example.com"
to = ["subscriber@example.com"]
msg = EmailMultiAlternatives(subject, text_content, from_email, to)
msg.attach_alternative(html_content, "text/html")
msg.send()
Configuring Mailers
The delivery mechanism is controlled by the MAILERS setting in your settings.py. This allows you to define multiple backends (e.g., one for marketing, one for transactional emails).
# settings.py
MAILERS = {
"default": {
"BACKEND": "django.core.mail.backends.smtp.EmailBackend",
"OPTIONS": {
"host": "smtp.sendgrid.net",
"port": 587,
"use_tls": True,
"username": "apikey",
"password": "your_api_key",
},
},
"console": {
"BACKEND": "django.core.mail.backends.console.EmailBackend",
},
}
Selecting a Mailer at Runtime
When calling .send(), you can specify which configured mailer to use by passing the using argument with the alias defined in your MAILERS setting.
# Uses the 'console' mailer instead of 'default'
email.send(using="console")
You can also access mailers directly via the mailers handler:
from django.core.mail import mailers
# Get the console mailer instance
mailer = mailers["console"]
mailer.send_messages([email])
Adding Attachments
You can attach files using attach() (for content in memory) or attach_file() (for files on disk).
# Attaching a file from the filesystem
email.attach_file("/path/to/report.pdf")
# Attaching content directly
email.attach("invoice.txt", "Invoice content...", "text/plain")
Built-in Backends
This project provides several backends for different environments:
| Backend Class | Purpose |
|---|---|
django.core.mail.backends.smtp.EmailBackend | Sends emails via an SMTP server (Production). |
django.core.mail.backends.console.EmailBackend | Writes emails to stdout (Development). |
django.core.mail.backends.filebased.EmailBackend | Saves emails as files in a directory. Requires file_path in OPTIONS. |
django.core.mail.backends.locmem.EmailBackend | Stores emails in django.core.mail.outbox (Testing). |
django.core.mail.backends.dummy.EmailBackend | Does nothing; returns success (Testing). |
Troubleshooting and Best Practices
- Header Validation: Do not include newlines in subject or header values. This project raises a
ValueError(formerlyBadHeaderError) if CRLF injection is detected. - Deprecation Warning: The
connectionargument inEmailMessageandsend()is deprecated. Use theusingargument with aMAILERSalias instead. - Deprecation Warning: Directly instantiating backend classes (e.g.,
EmailBackend()) is deprecated. Usemailers["alias"]to obtain backend instances. - Bcc Handling: Recipients in the
bcclist are included in the delivery but are automatically stripped from the message headers to ensure privacy. - Performance: For sending multiple emails, the backends support context manager usage to reuse a single network connection:
with mailers["default"] as connection:
for msg in bulk_messages:
connection.send_messages([msg])