From 9b4433948714d5c43bab96b08bb6618c22512000 Mon Sep 17 00:00:00 2001 From: Johannes Maron Date: Wed, 20 May 2026 14:44:43 +0200 Subject: [PATCH 1/5] Refs #36825 -- Fixed regression in CSPSeleniumTestCase. The CSP report test relied on the debug view having a CSP error, which has been fixed in 3e4e0db. This commit added a custom view to reintroduce the same error to verify the reporting behavior. Follow-up to 3e4e0db66961a48a080ff3ff91f6c0d954261366. --- tests/middleware/test_csp.py | 7 ------- tests/middleware/urls.py | 3 +-- tests/middleware/views.py | 15 ++++++++++++++- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/tests/middleware/test_csp.py b/tests/middleware/test_csp.py index baf04d7650c6..fcdb9a1c1565 100644 --- a/tests/middleware/test_csp.py +++ b/tests/middleware/test_csp.py @@ -177,13 +177,6 @@ def test_csp_override_both_decorator(self): @override_settings( ROOT_URLCONF="middleware.urls", - SECURE_CSP_REPORT_ONLY={ - "default-src": [CSP.NONE], - "img-src": [CSP.SELF], - "script-src": [CSP.SELF], - "style-src": [CSP.SELF], - "report-uri": "/csp-report/", - }, ) @modify_settings( MIDDLEWARE={"append": "django.middleware.csp.ContentSecurityPolicyMiddleware"} diff --git a/tests/middleware/urls.py b/tests/middleware/urls.py index bbd68d205075..b14343bdd6f9 100644 --- a/tests/middleware/urls.py +++ b/tests/middleware/urls.py @@ -1,5 +1,4 @@ from django.urls import path, re_path -from django.views.debug import default_urlconf from . import views @@ -13,7 +12,7 @@ path("sensitive_fbv/", views.sensitive_fbv), path("sensitive_cbv/", views.SensitiveCBV.as_view()), # Used in CSP tests. - path("csp-failure/", default_urlconf), + path("csp-failure/", views.csp_failure), path("csp-report/", views.csp_report_view), path("csp-base/", views.empty_view), path("csp-nonce/", views.csp_nonce), diff --git a/tests/middleware/views.py b/tests/middleware/views.py index 716ddec5fdae..f7047f57cc25 100644 --- a/tests/middleware/views.py +++ b/tests/middleware/views.py @@ -5,7 +5,7 @@ from django.middleware.csp import get_nonce from django.utils.csp import CSP from django.utils.decorators import method_decorator -from django.views.debug import technical_500_response +from django.views.debug import default_urlconf, technical_500_response from django.views.decorators.common import no_append_slash from django.views.decorators.csp import csp_override, csp_report_only_override from django.views.decorators.csrf import csrf_exempt @@ -53,6 +53,19 @@ def csp_disabled_both(request): } +@csp_override( + { + "default-src": [CSP.NONE], + "img-src": [CSP.SELF], + "script-src": [CSP.SELF], + "style-src": [CSP.SELF], + "report-uri": "/csp-report/", + } +) +def csp_failure(request): + return default_urlconf(request) + + @csp_override(csp_policy_override) def csp_override_enforced(request): return HttpResponse() From 4ea7648df5f970593e3cbe222a19fb624fa7208c Mon Sep 17 00:00:00 2001 From: Mike Edmunds Date: Fri, 15 May 2026 14:21:09 -0700 Subject: [PATCH 2/5] Refs #35514 -- Cleaned up mailers docs. * Fixed typos related to automated EMAIL_PROVIDERS -> MAILERS renaming. * Clarified wording in some recently added/updated sections. * Removed deprecated, extraneous `fail_silently=False` from examples. * Moved EmailBackend API documentation out of "Email backends" intro into a dedicated section in email.txt. * Sorted MAILERS alphabetically in settings.txt. --- docs/howto/deployment/checklist.txt | 4 +- docs/howto/mailers-migration.txt | 69 ++++++++----- docs/ref/settings.txt | 142 +++++++++++++------------- docs/releases/6.1.txt | 4 +- docs/topics/email.txt | 152 +++++++++++++++------------- docs/topics/testing/tools.txt | 1 - 6 files changed, 199 insertions(+), 173 deletions(-) diff --git a/docs/howto/deployment/checklist.txt b/docs/howto/deployment/checklist.txt index d06b136e0ebd..0b2a49aed0d1 100644 --- a/docs/howto/deployment/checklist.txt +++ b/docs/howto/deployment/checklist.txt @@ -151,8 +151,8 @@ If your site sends emails, these values need to be set correctly. In development, email is often configured to be printed to the console or stored in a local file. For production, you probably want to send real email -messages. See :ref:`topic-email-configuration` for more information about -setting :setting:`MAILERS` to use production email services. +messages. :ref:`topic-email-configuration` has more information about setting +:setting:`MAILERS` to use production email servers. By default, Django sends email from ``webmaster@localhost`` and ``root@localhost``. However, many mail providers reject email from these diff --git a/docs/howto/mailers-migration.txt b/docs/howto/mailers-migration.txt index a3cdafc237ff..7e7df0821537 100644 --- a/docs/howto/mailers-migration.txt +++ b/docs/howto/mailers-migration.txt @@ -28,10 +28,11 @@ All Django projects that send email should: .. note:: - Test suites often use a non-functional email backend, such as the memory - backend that Django automatically :ref:`substitutes during tests - `. As a result, running tests won't uncover - deprecations that only appear when sending email in production. + Running tests may not identify all relevant deprecations. Test suites + often use a non-functional email backend (such as the memory backend that + Django :ref:`substitutes during tests `), so can + miss deprecation warnings that are only issued from the production email + configuration. Consider running with deprecation warnings enabled in production to catch those deprecations. Or carefully review your code (including third-party @@ -60,7 +61,8 @@ settings. In your project's settings, define a :setting:`MAILERS` dict with a "default": { "BACKEND": ..., # value of EMAIL_BACKEND setting "OPTIONS": { - ..., # values from other deprecated EMAIL_* settings + # values from other deprecated EMAIL_* settings + ..., }, }, } @@ -102,12 +104,14 @@ moved in a :setting:`MAILERS` configuration is: if a :setting:`MAILERS` configuration doesn't specify the ``"BACKEND"``). * :setting:`EMAIL_FILE_PATH` becomes ``"file_path"`` in ``"OPTIONS"`` (with ``"BACKEND"`` set to ``"django.core.mail.backends.filebased.EmailBackend"``). -* :setting:`EMAIL_HOST` becomes ``"host"`` in ``"OPTIONS"``. If your settings - didn't define :setting:`!EMAIL_HOST`, the default value was ``"localhost"`` - and you must add ``"host": "localhost"`` to the ``"OPTIONS"`` for an SMTP - mailer configuration. +* :setting:`EMAIL_HOST` becomes ``"host"`` in ``"OPTIONS"``. A ``"host"`` is + required for the SMTP email backend. If your settings didn't define + ``EMAIL_HOST``, set ``"host"`` to ``"localhost"`` (the default value for the + deprecated setting). * :setting:`EMAIL_HOST_PASSWORD` becomes ``"password"`` in ``"OPTIONS"``. -* :setting:`EMAIL_HOST_USER` becomes ``"username"`` in ``"OPTIONS"``. +* :setting:`EMAIL_HOST_USER` becomes ``"username"`` in ``"OPTIONS"``. (Note + ``"username"`` doesn't quite follow the naming pattern for the other + deprecated settings.) * :setting:`EMAIL_PORT` becomes ``"port"`` in ``"OPTIONS"``. It can be omitted if the connection uses the default port for its security (465 for SSL, 587 for TLS, or 25 for an unsecured SMTP connection). @@ -141,11 +145,13 @@ There are two deprecated features that are *not* supported when ``django.conf.settings`` (e.g., checking ``settings.EMAIL_BACKEND`` or using ``settings.EMAIL_HOST_USER``). * Calling :func:`mail.get_connection("path.to.EmailBackend") - <.mail.get_connection>` with a specific backend path. (Other uses of - ``get_connection()`` are still allowed, and will issue deprecation warnings.) + <.mail.get_connection>` with a specific backend path. (Other arguments to + ``get_connection()`` are still supported, and will simply issue deprecation + warnings when :setting:`MAILERS` is defined.) -If a third-party package does either of those, projects that use it cannot -upgrade to the :setting:`MAILERS` setting until the package has been updated. +If a third-party package does either of those, projects that use it will not be +able to change to the :setting:`MAILERS` setting until the package has been +updated. Solving "not available when MAILERS is defined" errors ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -164,6 +170,9 @@ need to remove :setting:`MAILERS` from your settings and replace it with the equivalent :ref:`deprecated email settings ` until the package has been updated. +If those errors are coming from your own code, see the other sections in this +migration guide for recommended updates. + .. _migrating-to-mailers-get-connection: Replacing ``get_connection()`` and ``connection`` arguments @@ -242,9 +251,10 @@ The ``fail_silently`` arguments to :func:`.send_mail`, :func:`.send_mass_mail`, :func:`.mail_admins`, :func:`.mail_managers`, and :meth:`.EmailMessage.send` are deprecated. -Handling of ``fail_silently`` varies depending on the email backend. A survey -of its use suggested that callers have several different expectations for its -behavior, many of which don't match the actual backend implementations. +Existing code using ``fail_silently`` seems to have several different +expectations for its behavior, many of which don't match the actual (and email +backend-dependent) implementation. Consider removing ``fail_silently`` entirely +if there is not a specific need for it. Calls with ``fail_silently=True`` should be updated with one of these options, depending on the caller's intent: @@ -254,23 +264,25 @@ depending on the caller's intent: ``except mail.MailerDoesNotExist: pass``. * To ignore *all* exceptions (e.g., to avoid cascading failures in an error - handler), wrap the send call in ``try:`` / ``except Exception: pass``. + handler that sends mail), wrap the send call in ``try:`` / ``except + Exception: pass``. * To ignore only SMTP-related errors, wrap the send call in ``try`` / ``except OSError: pass``. Note that this ignores both transient network glitches *and* SMTP configuration problems (just like the existing SMTP - backend ``fail_silently`` handling). + email backend ``fail_silently`` implementation). * To ignore end user typos in ``to`` addresses and other delivery problems, remove the ``fail_silently`` argument. Recipient errors are not generally detected at send time, so using ``fail_silently`` for this purpose doesn't accomplish anything and could mask other problems like configuration errors. - (In certain local delivery configurations, SMTP servers *may* report some - recipient errors at send time. Intercept + (In *local delivery* configurations, SMTP servers may report some recipient + errors at send time. If you are using ``fail_silently`` specifically to + ignore those errors, consider instead intercepting :exc:`~.smtplib.SMTPRecipientsRefused` and/or :exc:`~.smtplib.SMTPResponseException`\ s with particular ``smtp_code`` - values to detect those cases.) + values as a more precise filter.) * To create an email configuration that ignores certain backend-dependent errors and reuse it for multiple sending operations, create a custom @@ -308,14 +320,15 @@ compatibility with mailers. * Do not accept variable positional ``*args`` or pass them to superclass init. -The ``BaseEmailBackend`` superclass now defines a ``self.alias`` attribute. +The ``BaseEmailBackend`` superclass now initializes an ``alias`` attribute. This is useful for error messages (e.g., ``raise InvalidMailer(f"Bad host {host}", alias=self.alias)``), but should not be used for accessing ``settings.MAILERS`` directly. All OPTIONS for a mailer configuration are passed to backend init as keyword arguments. For reusable libraries that want to support compatibility with deprecated mail -functions and settings (similar to Django's built-in email backends): +functions and settings (similar to Django's built-in email backends), or that +still support Django versions earlier than 6.1: * A backend can detect it is being initialized without :setting:`MAILERS` by checking if ``self.alias is None``. (Django's built-in backends check this to @@ -336,9 +349,9 @@ functions and settings (similar to Django's built-in email backends): Replacing ``auth_user`` and ``auth_password`` --------------------------------------------- -The ``auth_user`` and ``auth_password`` arguments to :func:`.mail.send_mail` -and :func:`.mail.send_mass_mail` are deprecated. To replace them, define a -custom :setting:`MAILERS` configuration with ``"username"`` and ``"password"`` +The ``auth_user`` and ``auth_password`` arguments to :func:`.send_mail` and +:func:`.send_mass_mail` are deprecated. To replace them, define a custom +:setting:`MAILERS` configuration with ``"username"`` and ``"password"`` :setting:`"OPTIONS" `, and refer to that configuration with the ``using`` argument when sending mail. @@ -349,6 +362,7 @@ For example, to upgrade:: Add a custom :setting:`MAILERS` configuration in your settings:: MAILERS = { + # Existing default configuration. "default": { "OPTIONS": { "host": "smtp.example.com", @@ -356,6 +370,7 @@ Add a custom :setting:`MAILERS` configuration in your settings:: "password": "default-password", }, }, + # Duplicate the default configuration, changing the auth. "admin-config": { "OPTIONS": { "host": "smtp.example.com", diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 1a4de5b2dabb..97c16717fe8c 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -1392,77 +1392,6 @@ that are not allowed to visit any page, systemwide. Use this for bots/crawlers. This is only used if ``CommonMiddleware`` is installed (see :doc:`/topics/http/middleware`). -.. setting:: MAILERS - -``MAILERS`` ------------ - -.. versionadded:: 6.1 - -Default: no default until Django 7.0, then ``{}``. (See deprecation note -below.) - -A dictionary containing the settings for all email backends to be used with -Django. It is a nested dictionary that maps backend aliases to dictionaries -containing each mailer's backend import path and configuration options. -Example:: - - MAILERS = { - "default": { - "BACKEND": "django.core.mail.backends.smtp.EmailBackend", - "OPTIONS": { - "host": "smtp.example.net", - }, - }, - "newsletters": { - "BACKEND": "django.core.mail.backends.smtp.EmailBackend", - "OPTIONS": { - "host": "smtp.bulk-email-service.example.com", - "username": os.environ["BULK_EMAIL_SERVICE_ACCOUNT_ID"], - "password": os.environ["BULK_EMAIL_SERVICE_API_KEY"], - "use_tls": True, - }, - }, - } - -If you plan to send email through Django, configuring at least a ``"default"`` -mailer is recommended. Any number of additional mailers may also be specified. -Depending on which backend is used, other options may be required. - -See :ref:`topic-email-configuration` for more information. - -.. deprecated:: 6.1 - - In Django 7.0, the default value will change to ``{}`` (no defined email - mailers). Until then, if ``MAILERS`` is not defined in the - settings, Django will create a ``"default"`` mailer from the - deprecated :setting:`EMAIL_BACKEND` setting. Projects can opt into the - Django 7.0 behavior early by adding ``MAILERS`` and removing - ``EMAIL_BACKEND`` from their settings. - -.. setting:: MAILERS-BACKEND - -``BACKEND`` -~~~~~~~~~~~ - -Default: ``"django.core.mail.backends.smtp.EmailBackend"`` - -The Python import path to the email backend class. If ``"BACKEND"`` is omitted, -Django's :ref:`topic-email-smtp-backend` is used. - -See :ref:`topic-email-backends` for a list of Django's built-in backends and -pointers to community-maintained options. - -.. setting:: MAILERS-OPTIONS - -``OPTIONS`` -~~~~~~~~~~~ - -Default: ``{}`` - -Configuration parameters to pass to the email backend. The available and -required options vary depending on the backend. - .. setting:: EMAIL_BACKEND ``EMAIL_BACKEND`` @@ -2231,6 +2160,77 @@ Django project. Points at an instance of Python's :ref:`dictConfig If you set :setting:`LOGGING_CONFIG` to ``None``, the logging configuration process will be skipped. +.. setting:: MAILERS + +``MAILERS`` +----------- + +.. versionadded:: 6.1 + +Default: no default until Django 7.0, then ``{}``. (See deprecation note +below.) + +A dictionary containing the settings for all email backends to be used with +Django. It is a nested dictionary that maps backend aliases to dictionaries +containing each mailer's backend import path and configuration options. +Example:: + + MAILERS = { + "default": { + "BACKEND": "django.core.mail.backends.smtp.EmailBackend", + "OPTIONS": { + "host": "smtp.example.net", + }, + }, + "newsletters": { + "BACKEND": "django.core.mail.backends.smtp.EmailBackend", + "OPTIONS": { + "host": "smtp.bulk-email-service.example.com", + "username": os.environ["BULK_EMAIL_SERVICE_ACCOUNT_ID"], + "password": os.environ["BULK_EMAIL_SERVICE_API_KEY"], + "use_tls": True, + }, + }, + } + +If you plan to send email through Django, configuring at least a ``"default"`` +mailer is recommended. Any number of additional mailers may also be specified. +Depending on which backend is used, other options may be required. + +See :ref:`topic-email-configuration` for more information. + +.. deprecated:: 6.1 + + In Django 7.0, the default value will change to ``{}`` (no defined email + mailers). Until then, if ``MAILERS`` is not defined in the + settings, Django will create a ``"default"`` mailer from the + deprecated :setting:`EMAIL_BACKEND` setting. Projects can opt into the + Django 7.0 behavior early by adding ``MAILERS`` and removing + ``EMAIL_BACKEND`` from their settings. + +.. setting:: MAILERS-BACKEND + +``BACKEND`` +~~~~~~~~~~~ + +Default: ``"django.core.mail.backends.smtp.EmailBackend"`` + +The Python import path to the email backend class. If ``"BACKEND"`` is omitted, +Django's :ref:`topic-email-smtp-backend` is used. + +See :ref:`topic-email-backends` for a list of Django's built-in backends and +pointers to community-maintained options. + +.. setting:: MAILERS-OPTIONS + +``OPTIONS`` +~~~~~~~~~~~ + +Default: ``{}`` + +Configuration parameters to pass to the email backend. The available and +required options vary depending on the backend. + .. setting:: MANAGERS ``MANAGERS`` diff --git a/docs/releases/6.1.txt b/docs/releases/6.1.txt index 0415856c9572..12596d4fe6f1 100644 --- a/docs/releases/6.1.txt +++ b/docs/releases/6.1.txt @@ -99,8 +99,8 @@ with different options, similar to existing mechanisms for :setting:`CACHES`, "OPTIONS": {"host": "smtp.example.com", "use_tls": True}, }, "marketing": { - "BACKEND": "example_ses.EmailBackend", - "OPTIONS": {"region": "us-east-1"}, + "BACKEND": "example.third.party.EmailBackend", + "OPTIONS": {"region": "africa-1"}, }, } diff --git a/docs/topics/email.txt b/docs/topics/email.txt index ff5394e11474..18b682768f3b 100644 --- a/docs/topics/email.txt +++ b/docs/topics/email.txt @@ -8,8 +8,8 @@ Sending email Django provides wrappers for Python's :mod:`email` and :mod:`smtplib` modules to simplify composing and sending email. Django's email framework also supports swapping in different delivery mechanisms: you can direct email to the console -or a file during development, or use community-maintained solutions for sending -email directly through commercial email service providers. +or a file during development and an SMTP server or email service provider in +production. The code lives in the ``django.core.mail`` module. @@ -68,13 +68,12 @@ Configuring email ================= New Django projects are not configured to send email by default. Instead, email -is printed to ``stdout`` as a development aid (for projects created with +is printed to the console as a development aid (for projects created with :djadmin:`startproject`) or results in a ``MailerDoesNotExist`` error (when the :setting:`MAILERS` setting isn't defined). -To enable sending real email, you will need to tell Django how to send it by -creating or editing the :setting:`MAILERS` setting. For example, to send -through an SMTP server running on the local machine:: +Define edit the :setting:`MAILERS` setting to tell Django how to send email. +For example, to send through an SMTP server running on the local machine:: MAILERS = { "default": { @@ -85,29 +84,30 @@ through an SMTP server running on the local machine:: }, } -`SMTP`_ is supported by nearly all email service mailers and many hosting -environments. But there are other options: many commercial services offer APIs -with additional sending features, and during development or testing you might -not want to send email at all. - Django abstracts the email sending process into an "email backend" class. -:ref:`topic-email-backends` lists the email backends that come with Django: the -:ref:`SMTP backend ` for production use and several -others meant for development and testing. It also covers :ref:`third-party -packages ` and :ref:`custom backends -` if Django's built-in backends don't meet your -needs. - -Django's test runner automatically :ref:`overrides the email configuration -` during testing. It substitutes the :ref:`memory backend -` for each defined mailer, preventing email from -being sent and giving test cases access to the messages. +:ref:`topic-email-backends` lists the email backends that come with Django. + +The example above uses Django's :ref:`SMTP email backend +`, which sends using the standard `SMTP`_ protocol. +This backend is useful for many production configurations, including SMTP +servers in your own infrastructure and most commercial email service providers +(ESPs). There are also :ref:`third-party email backends +` available that integrate directly with ESP +APIs or add other sending features. + +During development or testing you often don't want to send email at all. +Django's test runner :ref:`automatically overrides ` the +:setting:`MAILERS` configuration to substitute Django's :ref:`memory email +backend `. This prevents test cases from sending +real email and gives them access to the messages that would have been sent. +:ref:`topics/email:Configuring email for development` discusses some other +approaches. .. versionchanged:: 6.1 In earlier releases, Django defaulted to sending email through an SMTP - server running on localhost (using the now-deprecated - :setting:`EMAIL_BACKEND` and related settings). + server running on localhost, using the now-deprecated + :setting:`EMAIL_BACKEND` and related settings. .. deprecated:: 6.1 @@ -123,9 +123,10 @@ being sent and giving test cases access to the messages. Multiple mailers ---------------- -Sometimes different types of email need to be sent in different ways: internal -vs. external email, different servers for users in different regions, different -services for transactional notifications and bulk marketing email, etc. +Sometimes different types of email need to be sent in different ways: e.g., +internal vs. external email, different SMTP servers for users in different +regions, using different services for transactional notifications and bulk +marketing email, etc. The :setting:`MAILERS` setting can define multiple mail configurations. For example:: @@ -162,8 +163,7 @@ This defines three mailer configurations: * ``"default"`` sends through an SMTP server at ``smtp.example.net`` with a TLS secured connection. It reads an account id and API key from environment variables and uses them as the SMTP authentication username and password. - (Many SMTP relay services use some variation of this authentication scheme. - Check your provider's documentation for specific options to use.) + (Many SMTP services use some variation of this authentication scheme.) * ``"notifications"`` sends through a hypothetical commercial email service, using a third-party EmailBackend that connects directly to their API. @@ -188,10 +188,10 @@ mailer configuration:: ) If ``using`` is not specified, Django uses the mailer defined for the -``"default"`` mailer configuration. +``"default"`` configuration. With reusable apps or Django features that send email for you, there may be an -option to use a specific mailer. For example, Django's logging +option to use a specific mailer configuration. For example, Django's logging :class:`~django.utils.log.AdminEmailHandler` allows specifying the mailer configuration in its ``using`` option. @@ -232,10 +232,9 @@ are required. The following parameters are optional, and must be given as keyword arguments if used. -* ``fail_silently``: A boolean. When it's ``False``, ``send_mail()`` will raise - an :exc:`smtplib.SMTPException` if an error occurs. See the :mod:`smtplib` - docs for a list of possible exceptions, all of which are subclasses of - :exc:`~smtplib.SMTPException`. +* ``fail_silently``: A boolean, default ``False``. If set ``True``, + ``send_mail()`` will suppress some errors during sending. (The exact + exceptions ignored depend on the email backend in use.) * ``auth_user``: The optional username to use to authenticate to the SMTP server. If this isn't provided, Django will use the value of the :setting:`EMAIL_HOST_USER` setting. @@ -269,7 +268,9 @@ can be ``0`` or ``1`` since it can only send one message). The ``fail_silently``, ``auth_user``, ``auth_password``, and ``connection`` arguments are deprecated. In most cases they can be replaced by ``using`` with an appropriate :setting:`MAILERS` configuration. See - :ref:`migrating-to-mailers`. + :ref:`migrating-to-mailers-fail-silently`, + :ref:`migrating-to-mailers-auth`, and + :ref:`migrating-to-mailers-get-connection`. .. versionchanged:: 6.1 @@ -318,7 +319,7 @@ mail server would be opened:: "from@example.com", ["second@test.com"], ) - send_mass_mail((message1, message2), fail_silently=False) + send_mass_mail((message1, message2)) The return value will be the number of successfully delivered messages. @@ -332,7 +333,9 @@ The return value will be the number of successfully delivered messages. The ``fail_silently``, ``auth_user``, ``auth_password``, and ``connection`` arguments are deprecated. In most cases they can be replaced by ``using`` with an appropriate :setting:`MAILERS` configuration. See - :ref:`migrating-to-mailers`. + :ref:`migrating-to-mailers-fail-silently`, + :ref:`migrating-to-mailers-auth`, and + :ref:`migrating-to-mailers-get-connection`. .. versionchanged:: 6.1 @@ -405,7 +408,9 @@ used. The ``fail_silently`` and ``connection`` arguments are deprecated. In most cases they can be replaced by ``using`` with an appropriate - :setting:`MAILERS` configuration. See :ref:`migrating-to-mailers`. + :setting:`MAILERS` configuration. See + :ref:`migrating-to-mailers-fail-silently` and + :ref:`migrating-to-mailers-get-connection`. .. versionchanged:: 6.1 @@ -436,7 +441,9 @@ used. The ``fail_silently`` and ``connection`` arguments are deprecated. In most cases they can be replaced by ``using`` with an appropriate - :setting:`MAILERS` configuration. See :ref:`migrating-to-mailers`. + :setting:`MAILERS` configuration. See + :ref:`migrating-to-mailers-fail-silently` and + :ref:`migrating-to-mailers-get-connection`. .. versionchanged:: 6.1 @@ -858,14 +865,13 @@ for that matter) is an expensive process. If you have a lot of emails to send, it makes sense to reuse an SMTP connection, rather than creating and destroying a connection every time you want to send an email. -There are two ways to tell an email backend to reuse a connection. Both -require an email backend instance obtained via :func:`get_connection`, which is -documented in :ref:`topic-email-backends`. +There are two ways to tell an email backend to reuse a connection. Both involve +obtaining an email backend instance from :data:`.mail.mailers` and using the +:ref:`backend's API `. -The first approach is to obtain an email backend instance from :data:`mailers` -and use its ``send_messages()`` method. This takes a list of -:class:`EmailMessage` (or subclass) instances, and sends them all using that -single connection. +The first approach is to use the backend's ``send_messages()`` method. This +takes a list of :class:`EmailMessage` (or subclass) instances, and sends them +all using that single connection. For example, if you have a function called ``get_notification_emails()`` that returns a list of :class:`EmailMessage` objects representing some periodic @@ -953,21 +959,6 @@ built-in backends don't meet your needs there are :ref:`third-party packages built-in backends to change its behavior, or even :ref:`write your own email backend `. -The email backend class has the following methods: - -* ``open()`` instantiates a long-lived email-sending connection. - -* ``close()`` closes the current email-sending connection. - -* ``send_messages(email_messages)`` sends a list of :class:`EmailMessage` - objects. If the connection is not open, this call will implicitly open the - connection, and close the connection afterward. If the connection is already - open, it will be left open after mail has been sent. - -It can also be used as a context manager, which will automatically call -``open()`` and ``close()`` as needed. An example is in -:ref:`topics-sending-multiple-emails`. - .. _topic-email-smtp-backend: SMTP backend @@ -1046,7 +1037,7 @@ Example:: When the :setting:`MAILERS` setting is not defined, Django uses the SMTP backend as the default mailer (the default :setting:`EMAIL_BACKEND`), connecting to localhost on port 25. This behavior will be removed in Django - 7.0, which will not have a default email mailer. + 7.0, which will not have a default mailer configuration. When the SMTP backend is used without :setting:`MAILERS` defined, the options listed above are obtained from the deprecated @@ -1064,11 +1055,12 @@ Example:: Directly instantiating an ``EmailBackend`` class is not recommended. Use :data:`mailers` to obtain a backend instance. - When constructed directly, the SMTP ``EmailBackend`` class accepts the - options listed above as keyword arguments. Default values come from the - corresponding, deprecated ``EMAIL_*`` settings. ``host`` is not required - and defaults to ``"localhost"``, and ``port`` defaults to ``25`` even if - ``use_tls`` or ``use_ssl`` is True. + When constructed directly (without going through :data:`mailers`), the SMTP + ``EmailBackend`` class accepts the options listed above as keyword + arguments. Default values come from the corresponding, deprecated + ``EMAIL_*`` settings. ``host`` is not required and defaults to + ``"localhost"``, and ``port`` defaults to ``25`` even if ``use_tls`` or + ``use_ssl`` is True. When the :setting:`MAILERS` setting is defined, attempting to directly create an SMTP ``EmailBackend`` will raise an ``AttributeError``. @@ -1122,8 +1114,8 @@ convenience that can be used during development. .. versionchanged:: 6.1 - The settings file created by :djadmin:`startproject` now sets up - :setting:`MAILERS` with the console backend as the default mailer. + The settings file created by :djadmin:`startproject` now defines + :setting:`MAILERS` with the console backend as the default configuration. .. _topic-email-file-backend: @@ -1319,6 +1311,26 @@ backends. 7.0. Switch to :data:`mailers[alias] `. See :ref:`migrating-to-mailers-get-connection` for migration suggestions. +.. _topic-email-backend-api: + +Email backend API +----------------- + +Instances of an email backend class have the following methods: + +* ``open()`` instantiates a long-lived email-sending connection. + +* ``close()`` closes the current email-sending connection. + +* ``send_messages(email_messages)`` sends a list of :class:`EmailMessage` + objects. If the connection is not open, this call will implicitly open the + connection, and close the connection afterward. If the connection is already + open, it will be left open after mail has been sent. + +A backend instance can also be used as a context manager, which will +automatically call ``open()`` and ``close()`` as needed. An example is in +:ref:`topics-sending-multiple-emails`. + Configuring email for development ================================= diff --git a/docs/topics/testing/tools.txt b/docs/topics/testing/tools.txt index b9e655adca93..da9dd38eeca3 100644 --- a/docs/topics/testing/tools.txt +++ b/docs/topics/testing/tools.txt @@ -2075,7 +2075,6 @@ and contents:: "Here is the message.", "from@example.com", ["to@example.com"], - fail_silently=False, ) # Test that one message has been sent. From 3ca621a38642bfd8fc2bfd308f489cc2d9e76fb0 Mon Sep 17 00:00:00 2001 From: Skyiesac Date: Tue, 31 Mar 2026 18:18:18 +0530 Subject: [PATCH 3/5] Fixed #36458 -- Trapped focus in the admin calendar and clock widgets. --- .../admin/static/admin/css/responsive.css | 12 -- .../admin/static/admin/css/widgets.css | 25 ++-- .../admin/js/admin/DateTimeShortcuts.js | 114 +++++++++++------- js_tests/admin/DateTimeShortcuts.test.js | 2 +- tests/admin_widgets/tests.py | 16 +++ 5 files changed, 106 insertions(+), 63 deletions(-) diff --git a/django/contrib/admin/static/admin/css/responsive.css b/django/contrib/admin/static/admin/css/responsive.css index 1a8a0ce60053..509b4fb9ff8e 100644 --- a/django/contrib/admin/static/admin/css/responsive.css +++ b/django/contrib/admin/static/admin/css/responsive.css @@ -780,18 +780,6 @@ button { overflow: visible; } - .calendarbox:before, - .clockbox:before { - content: ""; - position: fixed; - top: 50%; - left: 50%; - width: 100vw; - height: 100vh; - background: rgba(0, 0, 0, 0.75); - transform: translate(-50%, -50%); - } - .calendarbox > *, .clockbox > * { position: relative; diff --git a/django/contrib/admin/static/admin/css/widgets.css b/django/contrib/admin/static/admin/css/widgets.css index 928c81f37612..55d91035a3b3 100644 --- a/django/contrib/admin/static/admin/css/widgets.css +++ b/django/contrib/admin/static/admin/css/widgets.css @@ -345,6 +345,13 @@ table p.datetime { padding-left: 0; } +.datetimeshortcuts button { + border: none; + background: none; + padding: 0; + cursor: pointer; +} + .datetimeshortcuts .clock-icon, .datetimeshortcuts .date-icon { position: relative; @@ -360,8 +367,8 @@ table p.datetime { background-size: 24px auto; } -.datetimeshortcuts a:focus .clock-icon, -.datetimeshortcuts a:hover .clock-icon { +.datetimeshortcuts button:focus .clock-icon, +.datetimeshortcuts button:hover .clock-icon { background-position: 0 -24px; } @@ -371,8 +378,8 @@ table p.datetime { top: -1px; } -.datetimeshortcuts a:focus .date-icon, -.datetimeshortcuts a:hover .date-icon { +.datetimeshortcuts button:focus .date-icon, +.datetimeshortcuts button:hover .date-icon { background-position: 0 -24px; } @@ -426,7 +433,8 @@ span.clearable-file-input label { .calendarbox, .clockbox { - margin: 5px auto; + margin: 0; + padding: 0; font-size: 0.75rem; width: 19em; text-align: center; @@ -436,11 +444,12 @@ span.clearable-file-input label { border-radius: 4px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15); overflow: hidden; - position: relative; + position: absolute; } -.clockbox { - width: auto; +.calendarbox::backdrop, +.clockbox::backdrop { + background: rgba(0, 0, 0, 0.75); } .calendar { diff --git a/django/contrib/admin/static/admin/js/admin/DateTimeShortcuts.js b/django/contrib/admin/static/admin/js/admin/DateTimeShortcuts.js index f6c61010d5e9..58a3523b009a 100644 --- a/django/contrib/admin/static/admin/js/admin/DateTimeShortcuts.js +++ b/django/contrib/admin/static/admin/js/admin/DateTimeShortcuts.js @@ -136,8 +136,8 @@ e.preventDefault(); DateTimeShortcuts.handleClockQuicklink(num, -1); }); - const clock_link = document.createElement("a"); - clock_link.href = "#"; + const clock_link = document.createElement("button"); + clock_link.type = "button"; clock_link.id = DateTimeShortcuts.clockLinkName + num; clock_link.addEventListener("click", function (e) { e.preventDefault(); @@ -169,7 +169,7 @@ // Create clock link div // // Markup looks like: - // + // - const clock_box = document.createElement("div"); - clock_box.style.display = "none"; - clock_box.style.position = "absolute"; + const clock_box = document.createElement("dialog"); clock_box.className = "clockbox module"; clock_box.id = DateTimeShortcuts.clockDivName + num; clock_box.setAttribute("role", "dialog"); @@ -239,7 +237,7 @@ }); document.addEventListener("keyup", function (event) { - if (event.which === 27) { + if (event.key === "Escape") { // ESC key closes popup DateTimeShortcuts.dismissClock(num); event.preventDefault(); @@ -261,25 +259,17 @@ } else { // since style's width is in em, it'd be tough to calculate // px value of it. let's use an estimated px for now - clock_box.style.left = findPosX(clock_link) - 110 + "px"; + clock_box.style.right = findPosX(clock_link) - 110 + "px"; } clock_box.style.top = Math.max(0, findPosY(clock_link) - 30) + "px"; // Show the clock box - clock_box.style.display = "block"; - document.addEventListener( - "click", - DateTimeShortcuts.dismissClockFunc[num], - ); + clock_box.showModal(); }, dismissClock: function (num) { - document.getElementById( - DateTimeShortcuts.clockDivName + num, - ).style.display = "none"; - document.removeEventListener( - "click", - DateTimeShortcuts.dismissClockFunc[num], - ); + document + .getElementById(DateTimeShortcuts.clockDivName + num) + .close(); }, handleClockQuicklink: function (num, val) { let d; @@ -333,8 +323,8 @@ e.preventDefault(); DateTimeShortcuts.handleCalendarQuickLink(num, 0); }); - const cal_link = document.createElement("a"); - cal_link.href = "#"; + const cal_link = document.createElement("button"); + cal_link.type = "button"; cal_link.id = DateTimeShortcuts.calendarLinkName + num; cal_link.addEventListener("click", function (e) { e.preventDefault(); @@ -367,7 +357,7 @@ // // Markup looks like: // - //