1.The email API, and the backend underneath it
Django ships one email API with two entry points. `send_mail(subject, message, from_email, recipient_list)` is the one-liner, and it returns the number of messages delivered — `1` or `0`, because it can only send one. `EmailMessage` is the object you build when you need more than that: cc, bcc, `reply_to`, extra headers, attachments. `EmailMultiAlternatives` adds `attach_alternative(html, "text/html")`, which is how you send an HTML email that still has a plain-text body. Underneath both sits a **backend**, chosen by the `EMAIL_BACKEND` setting, and swapping it is how the same code writes to your console in development and to an SMTP server in production.
Key features
- `send_mail()` returns the number of messages delivered — `0` or `1`, since it sends only one
- `EmailMessage(subject, body, from_email, to, bcc=, cc=, reply_to=, headers=, attachments=, connection=)`
- HTML mail is `EmailMultiAlternatives` plus `attach_alternative(html, "text/html")` — the body stays plain text
- `attach(filename, content, mimetype=None)` guesses the type from the filename when `mimetype` is omitted
- `EMAIL_BACKEND` swaps SMTP for `console`, `filebased`, `locmem` (tests) or `dummy` without touching call sites
- `fail_silently=True` swallows the exception — including the one that means nothing was sent
The backends, and what each one is actually for
Which class to reach for
- Your code — builds a message: subject, text body, HTML alternative, attachments
- EmailMessage / EmailMultiAlternatives — knows nothing about how it will travel — that is the point
- Connection — get_connection() — open it yourself and one handshake serves the whole batch
- Backend — EMAIL_BACKEND — smtp in production · console/filebased in dev · locmem in tests · dummy for never
- An SMTP server, stdout, a file, or a list — the only layer that differs between your laptop and production
Command
send_mail(subject, message, from_email, recipient_list, fail_silently=False, html_message=None)`send_mail` returns a count (1 or 0), not a boolean; `EmailMessage` is the object form; `EmailMultiAlternatives.attach_alternative(html, "text/html")` is all "HTML email" means — the plain-text body stays. `EMAIL_BACKEND` swaps SMTP for console, filebased, locmem or dummy without touching a single call site, so tests assert on `mail.outbox`. Open a connection with `get_connection()` before a batch, or you pay one SMTP handshake per message. Treat `fail_silently=True` as data loss you agreed to.