plain.email
Send emails from your Plain application using SMTP, console output, or file-based backends.
Overview
You can send emails using the send_mail function for simple cases, or use the EmailMessage and EmailMultiAlternatives classes for more control. For template-based emails, the TemplateEmail class renders HTML templates automatically.
Sending a simple email
The send_mail function is the easiest way to send an email.
from plain.email import send_mail
send_mail(
subject="Welcome!",
message="Thanks for signing up.",
from_email="[email protected]",
recipient_list=["[email protected]"],
)
To include an HTML version along with the plain text:
send_mail(
subject="Welcome!",
message="Thanks for signing up.",
from_email="[email protected]",
recipient_list=["[email protected]"],
html_message="<h1>Thanks for signing up!</h1>",
)
Sending HTML emails
For more control over multipart emails, use EmailMultiAlternatives.
from plain.email import EmailMultiAlternatives
email = EmailMultiAlternatives(
subject="Your order confirmation",
body="Your order #123 has been confirmed.",
from_email="[email protected]",
to=["[email protected]"],
)
email.attach_alternative("<h1>Order #123 Confirmed</h1>", "text/html")
email.send()
Template-based emails
The TemplateEmail class renders emails from template files. You provide a template name, and it looks for corresponding files in your templates/email/ directory:
email/{template}.html- HTML content (required)email/{template}.txt- Plain text content (optional, falls back to stripping HTML tags)email/{template}.subject.txt- Subject line (optional)
from plain.email import TemplateEmail
email = TemplateEmail(
template="welcome",
context={"user_name": "Alice"},
to=["[email protected]"],
)
email.send()
With these template files:
<!-- templates/email/welcome.html -->
<h1>Welcome, {{ user_name }}!</h1>
<p>We're glad you're here.</p>
{# templates/email/welcome.subject.txt #}
Welcome to our app, {{ user_name }}!
You can subclass TemplateEmail to customize the template context by overriding get_template_context().
Attachments
You can attach files to any email message.
from plain.email import EmailMessage
email = EmailMessage(
subject="Your report",
body="Please find your report attached.",
to=["[email protected]"],
)
# Attach content directly
email.attach("report.csv", csv_content, "text/csv")
# Or attach a file from disk
email.attach_file("/path/to/report.pdf")
email.send()
Settings
| Setting | Default | Env var |
|---|---|---|
EMAIL_BACKEND |
Required | PLAIN_EMAIL_BACKEND |
EMAIL_DEFAULT_FROM |
Required | PLAIN_EMAIL_DEFAULT_FROM |
EMAIL_DEFAULT_REPLY_TO |
None |
PLAIN_EMAIL_DEFAULT_REPLY_TO |
EMAIL_HOST |
"localhost" |
PLAIN_EMAIL_HOST |
EMAIL_PORT |
587 |
PLAIN_EMAIL_PORT |
EMAIL_HOST_USER |
"" |
PLAIN_EMAIL_HOST_USER |
EMAIL_HOST_PASSWORD |
"" |
PLAIN_EMAIL_HOST_PASSWORD |
EMAIL_USE_TLS |
True |
PLAIN_EMAIL_USE_TLS |
EMAIL_USE_SSL |
False |
PLAIN_EMAIL_USE_SSL |
EMAIL_TIMEOUT |
None |
PLAIN_EMAIL_TIMEOUT |
EMAIL_SSL_CERTFILE |
None |
PLAIN_EMAIL_SSL_CERTFILE |
EMAIL_SSL_KEYFILE |
None |
PLAIN_EMAIL_SSL_KEYFILE |
EMAIL_USE_LOCALTIME |
False |
PLAIN_EMAIL_USE_LOCALTIME |
See default_settings.py for more details.
Email backends
The EMAIL_BACKEND setting controls how emails are sent. Plain includes three backends.
SMTP backend
The default backend sends emails via SMTP.
EMAIL_BACKEND = "plain.email.backends.smtp.EmailBackend"
Console backend
Prints emails to the console instead of sending them. Useful during development.
EMAIL_BACKEND = "plain.email.backends.console.EmailBackend"
File-based backend
Writes emails to files in a directory. Useful for testing and debugging.
EMAIL_BACKEND = "plain.email.backends.filebased.EmailBackend"
EMAIL_FILE_PATH = "/path/to/email-output"
Each email is saved to a timestamped .log file in the specified directory.
FAQs
How do I send to multiple recipients efficiently?
Use send_mass_mail to send multiple messages over a single connection:
from plain.email import send_mass_mail
messages = (
("Subject 1", "Body 1", "[email protected]", ["[email protected]"]),
("Subject 2", "Body 2", "[email protected]", ["[email protected]"]),
)
send_mass_mail(messages)
How do I reuse a connection for multiple emails?
Use the backend as a context manager:
from plain.email import get_connection, EmailMessage
with get_connection() as connection:
for user in users:
email = EmailMessage(
subject="Hello",
body="Hi there!",
to=[user.email],
connection=connection,
)
email.send()
How do I add custom headers?
Pass a headers dict to any email class:
email = EmailMessage(
subject="Hello",
body="Content",
to=["[email protected]"],
headers={"X-Custom-Header": "value", "Reply-To": "[email protected]"},
)
How do I create a custom email backend?
Subclass BaseEmailBackend and implement the send_messages method:
from plain.email.backends.base import BaseEmailBackend
class MyBackend(BaseEmailBackend):
def send_messages(self, email_messages):
# Your sending logic here
return len(email_messages)
Installation
Install the plain.email package from PyPI:
uv add plain.email
Add plain.email to your INSTALLED_PACKAGES and configure the required settings:
# settings.py
INSTALLED_PACKAGES = [
# ...
"plain.email",
]
EMAIL_BACKEND = "plain.email.backends.smtp.EmailBackend"
EMAIL_DEFAULT_FROM = "[email protected]"
# For SMTP (adjust for your mail provider)
EMAIL_HOST = "smtp.example.com"
EMAIL_PORT = 587
EMAIL_HOST_USER = "your-username"
EMAIL_HOST_PASSWORD = "your-password"
EMAIL_USE_TLS = True
For local development, use the console backend to see emails in your terminal:
EMAIL_BACKEND = "plain.email.backends.console.EmailBackend"