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"