Sending Email

Django's email framework is a thin wrapper over Python's SMTP tools that lets you compose and deliver messages — plain text or HTML — through a configurable backend with one function call.

Learn Sending Email in our free Django course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.

Part of the free Django course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

In this lesson you'll fire off a quick message with send_mail , take full control with EmailMessage , preview mail in the console backend, and build templated HTML emails using EmailMultiAlternatives and render_to_string .

The quickest way to send mail is send_mail(subject, message, from_email, recipient_list) . Where that mail goes is decided by EMAIL_BACKEND . In development you use the console backend , which prints the full email to your terminal instead of contacting a real server.

When you need more than a basic message, build an EmailMessage object. You can add cc , bcc , reply_to , custom headers, and attachments, then call .send() . It's the class send_mail uses under the hood.

For rich email you send both a plain-text and an HTML version using EmailMultiAlternatives . You render each body from a template with render_to_string , set the plain text as the body, and attach the HTML as an alternative. Clients pick whichever they support.

send_mail returns how many messages were sent. Fill in the blank so the return value equals the number of recipients in the list.

❌ Connection refused / SMTP error in development

You're using the SMTP backend with no mail server running locally.

✅ Fix: set EMAIL_BACKEND to the console backend while developing so mail just prints.

You passed a bare string for the recipients, e.g. recipient_list="ada@example.com" .

✅ Fix: always wrap addresses in a list — ["ada@example.com"] .

You put HTML into the plain message argument instead of attaching it as an alternative.

✅ Fix: use EmailMultiAlternatives and attach_alternative(html, "text/html") .

Write a function that renders an order-confirmation subject and body from a context dictionary, then returns the full payload send_mail would deliver.

Lesson complete — your app can talk to the world!

You can send a quick message with send_mail , take full control with EmailMessage , preview mail through the console backend, and build templated HTML email with EmailMultiAlternatives and render_to_string .

🚀 Up next: The Caching Framework — make pages fast by storing expensive results with cache.set and @cache_page .

Practice quiz

Which function is the simplest way to send a plain-text email?

  • mail()
  • send_mail()
  • deliver()
  • EmailMessage.send_now()

Answer: send_mail(). send_mail() is a convenience function taking subject, message, from, and recipient list.

What arguments does send_mail() take in order?

  • recipient_list, subject, message, from_email
  • from_email, subject, recipient_list, message
  • subject, message, from_email, recipient_list
  • message, subject, recipient_list, from_email

Answer: subject, message, from_email, recipient_list. send_mail(subject, message, from_email, recipient_list) is the standard signature.

Which class gives full control over cc, bcc, and attachments?

  • EmailMessage
  • send_mail
  • SimpleMail
  • MailSender

Answer: EmailMessage. EmailMessage is the underlying class exposing cc, bcc, reply-to, headers, and attachments.

Which EMAIL_BACKEND prints emails to the terminal in development?

  • smtp.EmailBackend
  • filebased.EmailBackend
  • dummy.EmailBackend
  • console.EmailBackend

Answer: console.EmailBackend. django.core.mail.backends.console.EmailBackend prints the full email to the console.

Which class lets you send both plain-text and HTML versions?

  • EmailMultiAlternatives
  • HtmlEmail
  • send_mail with html=True
  • EmailMessage.as_html()

Answer: EmailMultiAlternatives. EmailMultiAlternatives lets you attach_alternative(html, 'text/html') alongside the plain body.

What does render_to_string return?

  • An HttpResponse
  • The rendered template as a Python string
  • A template object
  • A file path

Answer: The rendered template as a Python string. render_to_string renders a template with a context and returns the output as a string, ideal for email bodies.

How do you add the HTML version on an EmailMultiAlternatives message?

  • msg.set_html(html)
  • msg.html = html
  • msg.attach_alternative(html, 'text/html')
  • msg.add_html(html)

Answer: msg.attach_alternative(html, 'text/html'). attach_alternative(html_content, 'text/html') adds the HTML alternative.

Which backend do you switch to for sending real mail in production?

  • console.EmailBackend
  • dummy.EmailBackend
  • locmem.EmailBackend
  • smtp.EmailBackend

Answer: smtp.EmailBackend. The SMTP backend, configured with EMAIL_HOST, EMAIL_PORT, and credentials, sends real mail.

When should you use EmailMessage instead of send_mail?

  • When you need attachments, cc, or bcc
  • For every email
  • Only in development
  • Never — send_mail does everything

Answer: When you need attachments, cc, or bcc. Use EmailMessage when you need extra features like attachments, cc, bcc, or custom headers.

What does send_mail() return?

  • The EmailMessage object
  • None
  • The number of messages successfully sent
  • The recipient list

Answer: The number of messages successfully sent. send_mail() returns the number of successfully delivered messages.