Skip to content

Django Sending Emails

In this section, we will learn how to send emails in Django. We will cover how to configure email settings, create email templates, and send emails from views. Sending emails is a common requirement in web applications, whether for user registration, password resets, or notifications.

To test email-sending functionality in your Django application, you can set up a local SMTP server using Docker. This allows you to capture and view emails sent from your application without actually sending them to real email addresses. We will use smtp4dev, a simple SMTP server designed for development and testing purposes.

Official Docs: https://github.com/rnwood/smtp4dev

  1. Create a docker-compose.yml file with the following content to set up the smtp4dev service:
    # docker-compose.yml
    services:
    smtp4dev:
    image: rnwood/smtp4dev:v3
    ports:
    - '127.0.0.1:5000:80' # Web UI (localhost only)
    - '127.0.0.1:2525:25' # SMTP (localhost only for development)
    - '127.0.0.1:1143:143' # IMAP (localhost only for development)
    - '127.0.0.1:1110:110' # POP3 (localhost only for development)
  2. Run the following command in your terminal to start the SMTP server:
    Terminal window
    cd path/to/your/project
    docker compose up

After running the above command, you can access the SMTP server’s web interface at http://localhost:5000 or http://127.0.0.1:5000. This interface allows you to view all the emails that have been sent through the server, making it easy to test your email functionality without sending real emails.

Django provides several built-in email backends that you can use to send emails. The most commonly used backend is the SMTP backend, which allows you to send emails through an SMTP server. Other backends include the console backend (which prints emails to the console), the file backend (which saves emails to a file), and the in-memory backend (which stores emails in memory for testing purposes).

  1. SMTP Backend: This is the default backend that sends emails through an SMTP server. You can configure it with your email provider’s SMTP settings.
  2. Console Backend: This backend is useful for development and testing. It prints the email content to the console instead of sending it.
  3. File Backend: This backend saves emails to a specified file on the filesystem.
  4. InMemory Backend: This backend stores emails in memory for testing purposes.
  5. Dummy Backend: This backend does nothing with the emails. It’s useful for testing when you don’t want to send or store emails.

To configure email settings in Django, add the following settings to your settings.py file:

# settings.py
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
# or for testing purposes, you can use the console backend
# EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
EMAIL_HOST = "localhost" # Use your SMTP server's address
EMAIL_HOST_USER = "" # SMTP username (if required)
EMAIL_HOST_PASSWORD = "" # SMTP password (if required)
EMAIL_PORT = 2525 # Host SMTP port for local smtp4dev mapping
DEFAULT_FROM_EMAIL = "noreply@example.com" # Default sender email address
Settings for smtp4dev

If you’re using smtp4dev for testing, you can use the following settings:

# settings.py
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "localhost"
EMAIL_HOST_USER = ""
EMAIL_HOST_PASSWORD = ""
EMAIL_PORT = 2525
DEFAULT_FROM_EMAIL = "noreply@example.com"

There are several ways to send emails from views in Django.

Types of Email Sending Methods:

  • send_mail: A simple function for sending a single email. It opens new connections for each email.
  • send_mass_mail: A function for sending multiple emails in a single call.
  • mail_admins: A function for sending emails to site administrators.
  • EmailMessage class: A more flexible way to create and send emails, allowing for attachments and custom headers.
from django.core.mail import send_mail
from django.http import HttpResponse
from django.core.mail import BadHeaderError
def send_email_view(request):
subject = "Test Email"
message = "This is a test email sent from Django."
from_email = "noreply@example.com"
recipient_list = ["user@example.com"]
try:
# send_mail arguments: subject, message, from_email, recipient_list
send_mail(subject, message, from_email, recipient_list)
except BadHeaderError:
return HttpResponse("Invalid header found.")
except Exception as e:
return HttpResponse(f"Error sending email: {e}")
return HttpResponse("Email sent successfully.")
from django.core.mail import send_mass_mail
from django.http import HttpResponse
from django.core.mail import BadHeaderError
def send_email_view(request):
datatuple = [
("Subject 1", "Message 1", "noreply@example.com", ["user@example.com"]),
("Subject 2", "Message 2", "noreply@example.com", ["user@example.com"]),
]
try:
send_mass_mail(datatuple)
except BadHeaderError:
return HttpResponse("Invalid header found.")
except Exception as e:
return HttpResponse(f"Error sending email: {e}")
return HttpResponse("Email sent successfully.")
# settings.py
ADMINS = [("Admin", "admin@example.com")]
from django.core.mail import mail_admins
from django.http import HttpResponse
from django.core.mail import BadHeaderError
def send_email_view(request):
subject = "Admin Notification"
message = "This is a notification email for admins."
try:
mail_admins(subject, message)
except BadHeaderError:
return HttpResponse("Invalid header found.")
except Exception as e:
return HttpResponse(f"Error sending email: {e}")
return HttpResponse("Email sent to admins successfully.")
from django.core.mail import EmailMessage
from django.http import HttpResponse
from django.core.mail import BadHeaderError
def send_email_view(request):
# EmailMessage arguments: subject, body, from_email, recipient_list
email = EmailMessage(
"Custom Email",
"This is a custom email with attachments.",
"noreply@example.com",
["user@example.com"],
)
# absolute path to the file you want to attach
email.attach_file("/path/to/attachment.pdf")
try:
email.send()
except BadHeaderError:
return HttpResponse("Invalid header found.")
except Exception as e:
return HttpResponse(f"Error sending email: {e}")
return HttpResponse("Custom email sent successfully.")

Generally, it’s a good practice to use email templates when sending emails in Django. This allows you to separate the email content from your views and makes it easier to manage and update your email content.

  1. Install

    Terminal window
    uv add django-templated-mail
  2. Create an html template in templates/emails directory, for example welcome_email.html:

    {% block subject %}Welcome to MySite!{% endblock %}
    {% block text_body %}
    {% comment %}This contains text content{% endcomment %}
    Thank you for visiting our site. We're excited to have you on board.
    {% endblock %}
    {% block html_body %}
    {% comment %}This contains HTML content{% endcomment %}
    <h1>Welcome to MySite, {{ name }}!</h1>
    <p>Thank you for visiting our site. We're excited to have you on board.</p>
    {% endblock %}
  3. Use the BaseEmailMessage class in your view to send the email:

    from templated_mail.mail import BaseEmailMessage
    from django.http import HttpResponse
    from django.core.mail import BadHeaderError
    def send_email_view(request):
    email = BaseEmailMessage(
    template_name="emails/welcome_email.html",
    context={"name": "John Doe"},
    )
    try:
    email.send(
    ["user@example.com"], # list of recipient email addresses
    from_email="support@example.com",
    )
    except BadHeaderError:
    return HttpResponse("Invalid header found.")
    except Exception as e:
    return HttpResponse(f"Error sending email: {e}")
    return HttpResponse("Templated email sent successfully.")

If you do not pass from_email, BaseEmailMessage falls back to DEFAULT_FROM_EMAIL.

Using Django’s Built-in Template Rendering

Section titled “Using Django’s Built-in Template Rendering”

If you want a simpler and more explicit approach, you can render the email body with Django’s template loader and send it with EmailMultiAlternatives.

from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
from django.utils.html import strip_tags
def send_email_view(request):
context = {"name": "John Doe"}
subject = render_to_string("emails/welcome/subject.txt", context).strip()
html_body = render_to_string("emails/welcome/body.html", context)
text_body = strip_tags(html_body)
email = EmailMultiAlternatives(
subject=subject,
body=text_body,
from_email="support@example.com",
to=["user@example.com"],
)
email.attach_alternative(html_body, "text/html")
email.send()

This pattern is useful when you want full control over the sender, subject, and both text and HTML bodies without relying on a helper package.