Skip to content

Django Sending Emails

In this section, we will learn how to send emails using Django. We’ll look at how to set up email settings, create email templates, and send emails from our views. Sending emails is something almost every web app needs at some point - for things like user sign-up, password resets, or notifications.

To test if your Django app’s email-sending actually works, you can set up a local SMTP server using Docker. This lets you see every email your app sends, without actually sending it to a real email address. We’ll use Mailpit, a simple SMTP server made just for development and testing.

Official Docs: https://mailpit.axllent.org/docs/

  1. Create a docker-compose.yml file with the following content to set up the Mailpit service:
    # docker-compose.yml
    services:
    mailpit:
    image: axllent/mailpit
    ports:
    - '127.0.0.1:8025:8025' # Web UI (localhost only)
    - '127.0.0.1:1025:1025' # SMTP (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

Once the command above is running, you can open the SMTP server’s web interface at http://localhost:8025 or http://127.0.0.1:8025. This page shows you every email that has been sent through the server, so you can easily test your email feature without sending out any real emails.

Django comes with a few built-in “email backends” you can use to send emails. The most common one is the SMTP backend, which sends emails through a real (or local test) SMTP server. There are other backends too: the console backend (which just prints the email in your terminal), the file backend (which saves the email into a file), and the in-memory backend (which keeps emails in memory, just for testing).

  1. SMTP Backend: This is the default backend, and it sends real emails through an SMTP server. You set it up using your email provider’s SMTP details.
  2. Console Backend: This one is great for development and testing. Instead of sending the email, it just prints the content straight to your terminal.
  3. File Backend: This backend saves each email into a file on your disk.
  4. InMemory Backend: This backend keeps emails in memory, which is handy when writing tests.
  5. Dummy Backend: This backend does nothing at all with the email. It’s useful when you want to test your code without sending or saving any email.

To set up email in Django, add the following settings inside 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 = 1025 # Host SMTP port for local Mailpit mapping
DEFAULT_FROM_EMAIL = "noreply@example.com" # Default sender email address
Settings for Mailpit

If you’re using Mailpit 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 = 1025
DEFAULT_FROM_EMAIL = "noreply@example.com"

There are a few different ways to send emails from your views in Django.

Types of Email Sending Methods:

  • send_mail: A simple function that sends one single email. It opens a brand new connection every time you use it.
  • send_mass_mail: A function that sends several emails in one go, using a single call.
  • mail_admins: A function made for sending emails to your site’s admin users.
  • EmailMessage class: A more flexible way to build and send an email, which also lets you add 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.")

It’s usually a good idea to use email templates when sending emails in Django. This way, the actual email content stays separate from your views, which makes it much easier to manage and update later on.

  1. Install

    Terminal window
    uv add django-templated-mail
  2. Create an html template in the templates/emails folder, 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 don’t pass in from_email, BaseEmailMessage will just use DEFAULT_FROM_EMAIL instead.

Using Django’s Built-in Template Rendering

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

If you’d rather keep things simple and more direct, you can render the email body using Django’s own template loader, and send it using 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 approach is great when you want full control over the sender, the subject, and both the plain text and HTML versions of the email, without relying on any extra helper package.