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.
SMTP Server Setup Using Docker
Section titled “SMTP Server Setup Using Docker”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/
- Create a
docker-compose.ymlfile with the following content to set up theMailpitservice:# docker-compose.ymlservices:mailpit:image: axllent/mailpitports:- '127.0.0.1:8025:8025' # Web UI (localhost only)- '127.0.0.1:1025:1025' # SMTP (localhost only for development) - Run the following command in your terminal to start the SMTP server:
Terminal window cd path/to/your/projectdocker compose up
docker run -p 127.0.0.1:8025:8025 -p 127.0.0.1:1025:1025 axllent/mailpitOnce 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.
Email Backends in Django
Section titled “Email Backends in Django”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).
Types of Email Backends
Section titled “Types of Email Backends”- 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.
- Console Backend: This one is great for development and testing. Instead of sending the email, it just prints the content straight to your terminal.
- File Backend: This backend saves each email into a file on your disk.
- InMemory Backend: This backend keeps emails in memory, which is handy when writing tests.
- 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.
Configuring Email Settings in Django
Section titled “Configuring Email Settings in Django”To set up email in Django, add the following settings inside your settings.py file:
# settings.pyEMAIL_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 addressEMAIL_HOST_USER = "" # SMTP username (if required)EMAIL_HOST_PASSWORD = "" # SMTP password (if required)EMAIL_PORT = 1025 # Host SMTP port for local Mailpit mappingDEFAULT_FROM_EMAIL = "noreply@example.com" # Default sender email addressSettings for Mailpit
If you’re using Mailpit for testing, you can use the following settings:
# settings.pyEMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"EMAIL_HOST = "localhost"EMAIL_HOST_USER = ""EMAIL_HOST_PASSWORD = ""EMAIL_PORT = 1025DEFAULT_FROM_EMAIL = "noreply@example.com"Sending Emails from Views
Section titled “Sending Emails from Views”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.EmailMessageclass: A more flexible way to build and send an email, which also lets you add attachments and custom headers.
Using send_mail Function
Section titled “Using send_mail Function”from django.core.mail import send_mailfrom django.http import HttpResponsefrom 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.")Using send_mass_mail Function
Section titled “Using send_mass_mail Function”from django.core.mail import send_mass_mailfrom django.http import HttpResponsefrom 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.")Using mail_admins Function
Section titled “Using mail_admins Function”# settings.py
ADMINS = [("Admin", "admin@example.com")]from django.core.mail import mail_adminsfrom django.http import HttpResponsefrom 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.")Using EmailMessage Class
Section titled “Using EmailMessage Class”from django.core.mail import EmailMessagefrom django.http import HttpResponsefrom 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.")Django Email Templates
Section titled “Django Email Templates”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.
Using Django Templated Mail
Section titled “Using Django Templated Mail”-
Install
Terminal window uv add django-templated-mail -
Create an html template in the
templates/emailsfolder, for examplewelcome_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 %} -
Use the
BaseEmailMessageclass in your view to send the email:from templated_mail.mail import BaseEmailMessagefrom django.http import HttpResponsefrom django.core.mail import BadHeaderErrordef 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 addressesfrom_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 EmailMultiAlternativesfrom django.template.loader import render_to_stringfrom 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.