From f8a9290f30e02b8183f656ae8d4ee6fb97791a84 Mon Sep 17 00:00:00 2001 From: Natalia <124304+nessita@users.noreply.github.com> Date: Tue, 14 Apr 2026 16:15:02 -0300 Subject: [PATCH 1/6] Fixed incomplete comment in email docs. --- docs/topics/email.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/topics/email.txt b/docs/topics/email.txt index 5946e3a9c14e..c4eac19b032f 100644 --- a/docs/topics/email.txt +++ b/docs/topics/email.txt @@ -903,7 +903,7 @@ manually open the connection, you can control when it is closed. For example:: ["to3@example.com"], ) - # Send the two emails in a single call - + # Send the two emails in a single call. connection.send_messages([email2, email3]) # The connection was already open so send_messages() doesn't close it. # We need to manually close the connection. From 339879620a105129951e9bcd999c74faa709e587 Mon Sep 17 00:00:00 2001 From: Mike Edmunds Date: Thu, 9 Apr 2026 16:18:25 -0700 Subject: [PATCH 2/6] Fixed broken link in email docs. --- docs/topics/email.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/topics/email.txt b/docs/topics/email.txt index c4eac19b032f..f7f1d124912b 100644 --- a/docs/topics/email.txt +++ b/docs/topics/email.txt @@ -815,7 +815,7 @@ convenience that can be used during development. highlighted on the `Community Ecosystem`_ page. The Django Packages `Email grid`_ has even more options for you! -.. _Community Ecosystem: https://www.djangoproject.com/community/ecosystem/#email-and-notifications +.. _Community Ecosystem: https://www.djangoproject.com/community/ecosystem/#email-notifications .. _Email grid: https://djangopackages.org/grids/g/email/ From d6e58765f559ba1d02974e11307a25e2e58d1585 Mon Sep 17 00:00:00 2001 From: Mike Edmunds Date: Tue, 7 Apr 2026 12:22:01 -0700 Subject: [PATCH 3/6] Refs #35514 -- Moved email docs examples to relevant section. The top-level "Examples" section of docs/topics/email.txt seemed intended to illustrate the difference between send_mail() and send_mass_mail(), not to provide general examples of sending email. Moved it into the existing "send_mass_mail() vs. send_mail()" section. (There's already a "Quick examples" section at the top of the page with general examples.) --- docs/topics/email.txt | 51 +++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/docs/topics/email.txt b/docs/topics/email.txt index f7f1d124912b..7411603e41c4 100644 --- a/docs/topics/email.txt +++ b/docs/topics/email.txt @@ -180,10 +180,31 @@ The return value will be the number of successfully delivered messages. ``send_mass_mail()`` vs. ``send_mail()`` ---------------------------------------- -The main difference between :func:`send_mass_mail` and :func:`send_mail` is -that :func:`send_mail` opens a connection to the mail server each time it's -executed, while :func:`send_mass_mail` uses a single connection for all of its -messages. This makes :func:`send_mass_mail` slightly more efficient. +The main difference between :func:`send_mass_mail` and repeatedly calling +:func:`send_mail` is that :func:`send_mail` opens a connection to the mail +server each time it's executed, while :func:`send_mass_mail` uses a single +connection for all of its messages. This makes :func:`send_mass_mail` slightly +more efficient. + +:func:`send_mail` with multiple ``to`` addresses sends a single email message, +with ``john@example.com`` and ``jane@example.com`` both appearing in the "To:" +field:: + + send_mail( + "Subject", + "Message.", + "from@example.com", + ["john@example.com", "jane@example.com"], + ) + +:func:`send_mass_mail` sends a separate message per ``datatuple`` element, so +``john@example.com`` and ``jane@example.com`` each receive their own email:: + + datatuple = ( + ("Subject", "Message.", "from@example.com", ["john@example.com"]), + ("Subject", "Message.", "from@example.com", ["jane@example.com"]), + ) + send_mass_mail(datatuple) ``mail_admins()`` ================= @@ -235,28 +256,6 @@ setting. Older versions ignored ``fail_silently=True`` when a ``connection`` was also provided. This now raises a ``TypeError``. -Examples -======== - -This sends a single email to john@example.com and jane@example.com, with them -both appearing in the "To:":: - - send_mail( - "Subject", - "Message.", - "from@example.com", - ["john@example.com", "jane@example.com"], - ) - -This sends a message to ``john@example.com`` and ``jane@example.com``, with -them both receiving a separate email:: - - datatuple = ( - ("Subject", "Message.", "from@example.com", ["john@example.com"]), - ("Subject", "Message.", "from@example.com", ["jane@example.com"]), - ) - send_mass_mail(datatuple) - Preventing header injection =========================== From 89d2298fbb9a682e82fdbb30263f94f18442bfae Mon Sep 17 00:00:00 2001 From: Mike Edmunds Date: Tue, 7 Apr 2026 12:40:45 -0700 Subject: [PATCH 4/6] Refs #35514 -- Grouped sending-related features in email docs. Introduced a top-level "Sending messages" section to group together send_mail(), send_mass_mail(), mail_admins(), mail_managers(), the EmailMessage and EmailMultiAlternatives classes, and other topics related to sending. --- docs/topics/email.txt | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/docs/topics/email.txt b/docs/topics/email.txt index 7411603e41c4..e42b9f091e91 100644 --- a/docs/topics/email.txt +++ b/docs/topics/email.txt @@ -67,13 +67,22 @@ set, are used to authenticate to the SMTP server, and the :setting:`EMAIL_USE_TLS` and :setting:`EMAIL_USE_SSL` settings control whether a secure connection is used. +.. _topic-email-sending: + +Sending messages +================ + +:mod:`!django.core.mail` provides functions for conveniently sending email, as +well as classes for building and sending more complex email messages with +attachments and multiple content types. + .. note:: The character set of email sent with ``django.core.mail`` will be set to the value of your :setting:`DEFAULT_CHARSET` setting. ``send_mail()`` -=============== +--------------- .. function:: send_mail(subject, message, from_email, recipient_list, *, fail_silently=False, auth_user=None, auth_password=None, connection=None, html_message=None) @@ -127,7 +136,7 @@ can be ``0`` or ``1`` since it can only send one message). This now raises a ``TypeError``. ``send_mass_mail()`` -==================== +-------------------- .. function:: send_mass_mail(datatuple, *, fail_silently=False, auth_user=None, auth_password=None, connection=None) @@ -178,7 +187,7 @@ The return value will be the number of successfully delivered messages. This now raises a ``TypeError``. ``send_mass_mail()`` vs. ``send_mail()`` ----------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The main difference between :func:`send_mass_mail` and repeatedly calling :func:`send_mail` is that :func:`send_mail` opens a connection to the mail @@ -207,7 +216,7 @@ field:: send_mass_mail(datatuple) ``mail_admins()`` -================= +----------------- .. function:: mail_admins(subject, message, *, fail_silently=False, connection=None, html_message=None) @@ -238,7 +247,7 @@ If ``html_message`` is provided, the resulting email will be a was also provided. This now raises a ``TypeError``. ``mail_managers()`` -=================== +------------------- .. function:: mail_managers(subject, message, *, fail_silently=False, connection=None, html_message=None) @@ -257,7 +266,7 @@ setting. was also provided. This now raises a ``TypeError``. Preventing header injection -=========================== +--------------------------- `Header injection`_ is a security exploit in which an attacker inserts extra email headers to control the "To:" and "From:" in email messages that your @@ -307,7 +316,7 @@ to "/contact/thanks/" when it's done:: .. _emailmessage-and-smtpconnection: The ``EmailMessage`` class -========================== +-------------------------- Django's :func:`send_mail` and :meth:`send_mass_mail` functions are actually thin wrappers that make use of the :class:`EmailMessage` class. @@ -333,9 +342,6 @@ method for sending a single email. If you need to send multiple messages, the email backend API :ref:`provides an alternative `. -``EmailMessage`` Objects ------------------------- - .. class:: EmailMessage The :class:`!EmailMessage` class is initialized with the following @@ -554,10 +560,10 @@ email backend API :ref:`provides an alternative * ``mimetype`` Sending alternative content types ---------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sending multiple content versions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ It can be useful to include multiple versions of the content in an email; the classic example is to send both text and HTML versions of a message. With @@ -628,7 +634,7 @@ Django's email library, you can do this using the * ``mimetype`` Updating the default content type -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ By default, the MIME type of the ``body`` parameter in an :class:`EmailMessage` is ``"text/plain"``. It is good practice to leave this alone, because it From 8f36420364265ea1635b6038770077c08cb9ee97 Mon Sep 17 00:00:00 2001 From: Mike Edmunds Date: Tue, 7 Apr 2026 12:47:03 -0700 Subject: [PATCH 5/6] Refs #35514 -- Moved EmailMessage class up in email docs. Moved the "Preventing header injection" discussion below sections on EmailMessage and related classes. --- docs/topics/email.txt | 96 +++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/docs/topics/email.txt b/docs/topics/email.txt index e42b9f091e91..bc558095d57b 100644 --- a/docs/topics/email.txt +++ b/docs/topics/email.txt @@ -265,54 +265,6 @@ setting. Older versions ignored ``fail_silently=True`` when a ``connection`` was also provided. This now raises a ``TypeError``. -Preventing header injection ---------------------------- - -`Header injection`_ is a security exploit in which an attacker inserts extra -email headers to control the "To:" and "From:" in email messages that your -scripts generate. - -The Django email functions outlined above all protect against header injection -by forbidding newlines in header values. If any ``subject``, ``from_email`` or -``recipient_list`` contains a newline (in either Unix, Windows or Mac style), -the email function (e.g. :func:`send_mail`) will raise :exc:`ValueError` and, -hence, will not send the email. It's your responsibility to validate all data -before passing it to the email functions. - -If a ``message`` contains headers at the start of the string, the headers will -be printed as the first bit of the email message. - -Here's an example view that takes a ``subject``, ``message`` and ``from_email`` -from the request's POST data, sends that to ``admin@example.com`` and redirects -to "/contact/thanks/" when it's done:: - - from django.core.mail import send_mail - from django.http import HttpResponse, HttpResponseRedirect - - - def send_email(request): - subject = request.POST.get("subject", "") - message = request.POST.get("message", "") - from_email = request.POST.get("from_email", "") - if subject and message and from_email: - try: - send_mail(subject, message, from_email, ["admin@example.com"]) - except ValueError: - return HttpResponse("Invalid header found.") - return HttpResponseRedirect("/contact/thanks/") - else: - # In reality we'd use a form class - # to get proper validation errors. - return HttpResponse("Make sure all fields are entered and valid.") - - -.. versionchanged:: 6.0 - - Older versions raised ``django.core.mail.BadHeaderError`` for some - invalid headers. This has been replaced with :exc:`!ValueError`. - -.. _Header injection: http://www.nyphp.org/phundamentals/8_Preventing-Email-Header-Injection.html - .. _emailmessage-and-smtpconnection: The ``EmailMessage`` class @@ -649,6 +601,54 @@ example:: msg.content_subtype = "html" # Main content is now text/html msg.send() +Preventing header injection +--------------------------- + +`Header injection`_ is a security exploit in which an attacker inserts extra +email headers to control the "To:" and "From:" in email messages that your +scripts generate. + +The Django email functions outlined above all protect against header injection +by forbidding newlines in header values. If any ``subject``, ``from_email`` or +``recipient_list`` contains a newline (in either Unix, Windows or Mac style), +the email function (e.g. :func:`send_mail`) will raise :exc:`ValueError` and, +hence, will not send the email. It's your responsibility to validate all data +before passing it to the email functions. + +If a ``message`` contains headers at the start of the string, the headers will +be printed as the first bit of the email message. + +Here's an example view that takes a ``subject``, ``message`` and ``from_email`` +from the request's POST data, sends that to ``admin@example.com`` and redirects +to "/contact/thanks/" when it's done:: + + from django.core.mail import send_mail + from django.http import HttpResponse, HttpResponseRedirect + + + def send_email(request): + subject = request.POST.get("subject", "") + message = request.POST.get("message", "") + from_email = request.POST.get("from_email", "") + if subject and message and from_email: + try: + send_mail(subject, message, from_email, ["admin@example.com"]) + except ValueError: + return HttpResponse("Invalid header found.") + return HttpResponseRedirect("/contact/thanks/") + else: + # In reality we'd use a form class + # to get proper validation errors. + return HttpResponse("Make sure all fields are entered and valid.") + + +.. versionchanged:: 6.0 + + Older versions raised ``django.core.mail.BadHeaderError`` for some + invalid headers. This has been replaced with :exc:`!ValueError`. + +.. _Header injection: http://www.nyphp.org/phundamentals/8_Preventing-Email-Header-Injection.html + .. _topic-email-backends: Email backends From 86cea4145dcd870231ea81f3cfe78fcbe02dbae4 Mon Sep 17 00:00:00 2001 From: Mike Edmunds Date: Tue, 7 Apr 2026 13:05:40 -0700 Subject: [PATCH 6/6] Refs #35514 -- Corrected email backends' outline levels in email docs. Moved documentation section for each email backend to be a direct child of "Email backends" (rather than subsections of "Obtaining an instance of an email backend"). Added a section header for "Third-party backends" to surface it in the outline and separate it from "Dummy backend." --- docs/topics/email.txt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/topics/email.txt b/docs/topics/email.txt index bc558095d57b..09d4b68b1ade 100644 --- a/docs/topics/email.txt +++ b/docs/topics/email.txt @@ -719,7 +719,7 @@ can :ref:`write your own email backend `. .. _topic-email-smtp-backend: SMTP backend -~~~~~~~~~~~~ +------------ .. class:: backends.smtp.EmailBackend(host=None, port=None, username=None, password=None, use_tls=None, fail_silently=False, use_ssl=None, timeout=None, ssl_keyfile=None, ssl_certfile=None, **kwargs) @@ -749,7 +749,7 @@ SMTP backend .. _topic-email-console-backend: Console backend -~~~~~~~~~~~~~~~ +--------------- Instead of sending out real emails the console backend just writes the emails that would be sent to the standard output. By default, the console @@ -766,7 +766,7 @@ convenience that can be used during development. .. _topic-email-file-backend: File backend -~~~~~~~~~~~~ +------------ The file backend writes emails to a file. A new file is created for each new session that is opened on this backend. The directory to which the files are @@ -784,7 +784,7 @@ convenience that can be used during development. .. _topic-email-memory-backend: In-memory backend -~~~~~~~~~~~~~~~~~ +----------------- The ``'locmem'`` backend stores messages in a special attribute of the ``django.core.mail`` module. The ``outbox`` attribute is created when the first @@ -804,7 +804,7 @@ Django's test runner :ref:`automatically uses this backend for testing .. _topic-email-dummy-backend: Dummy backend -~~~~~~~~~~~~~ +------------- As the name suggests the dummy backend does nothing with your messages. To specify this backend, put the following in your settings:: @@ -814,6 +814,9 @@ specify this backend, put the following in your settings:: This backend is not intended for use in production -- it is provided as a convenience that can be used during development. +Third-party backends +-------------------- + .. admonition:: There are community-maintained solutions too! Django has a vibrant ecosystem. There are email backends