diff --git a/django/core/checks/__init__.py b/django/core/checks/__init__.py index faca2a337d0b..7f2f21989daf 100644 --- a/django/core/checks/__init__.py +++ b/django/core/checks/__init__.py @@ -20,6 +20,7 @@ import django.core.checks.compatibility.django_4_0 # NOQA isort:skip import django.core.checks.database # NOQA isort:skip import django.core.checks.files # NOQA isort:skip +import django.core.checks.mail # NOQA isort:skip import django.core.checks.model_checks # NOQA isort:skip import django.core.checks.security.base # NOQA isort:skip import django.core.checks.security.csrf # NOQA isort:skip diff --git a/django/core/checks/mail.py b/django/core/checks/mail.py new file mode 100644 index 000000000000..49fcf0d8e4fc --- /dev/null +++ b/django/core/checks/mail.py @@ -0,0 +1,29 @@ +from django.conf import settings +from django.core.checks import Tags, Warning, register +from django.core.mail import DEFAULT_MAILER_ALIAS + + +@register(Tags.mail) +def check_mailers_default_alias(app_configs, **kwargs): + if not settings.is_overridden("MAILERS"): + return [] + + if DEFAULT_MAILER_ALIAS in settings.MAILERS: + return [] + + if settings.MAILERS: + hint = ( + f"Add a '{DEFAULT_MAILER_ALIAS}' entry to MAILERS, or pass 'using' when " + "sending email." + ) + else: + hint = f"Add a '{DEFAULT_MAILER_ALIAS}' entry to MAILERS." + + return [ + Warning( + f"Your MAILERS setting has no '{DEFAULT_MAILER_ALIAS}' entry. Sending " + "email without a valid mailer will fail.", + hint=hint, + id="mail.W001", + ) + ] diff --git a/django/core/checks/registry.py b/django/core/checks/registry.py index 257dadac41b6..32657ce12cfc 100644 --- a/django/core/checks/registry.py +++ b/django/core/checks/registry.py @@ -17,6 +17,7 @@ class Tags: compatibility = "compatibility" database = "database" files = "files" + mail = "mail" models = "models" security = "security" signals = "signals" diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt index 462736782325..3928057c84d8 100644 --- a/docs/ref/checks.txt +++ b/docs/ref/checks.txt @@ -84,6 +84,7 @@ Django's system checks are organized using the following tags: or if you specify configured database aliases using the ``--database`` option when calling the :djadmin:`check` command. * ``files``: Checks files related configuration. +* ``mail``: Checks email related configuration. * ``models``: Checks of model, field, and manager definitions. * ``security``: Checks security related configuration. * ``signals``: Checks on signal declarations and handler registrations. @@ -151,6 +152,15 @@ If you're using MySQL or MariaDB, the following checks will be performed: * **mysql.W003**: MySQL/MariaDB may not allow unique ``CharField``\s to have a ``max_length`` > 255. +Mail +---- + +The following checks verify that your :setting:`MAILERS` setting is correctly +configured: + +* **mail.W001**: Your :setting:`MAILERS` setting has no ``'default'`` entry. + Sending email without a valid mailer will fail. + Managing files -------------- diff --git a/docs/releases/6.1.txt b/docs/releases/6.1.txt index 03a9b8cdfd0a..c441709057e4 100644 --- a/docs/releases/6.1.txt +++ b/docs/releases/6.1.txt @@ -237,6 +237,12 @@ CSP :func:`~django.template.context_processors.csp` context processor is configured. See :ref:`csp-nonce-config` for setup instructions. +Email +~~~~~ + +* A new ``mail.W001`` system check warns when :setting:`MAILERS` is defined but + does not include a ``'default'`` entry. + Forms ~~~~~ diff --git a/tests/check_framework/test_mail.py b/tests/check_framework/test_mail.py new file mode 100644 index 000000000000..a67e147e9282 --- /dev/null +++ b/tests/check_framework/test_mail.py @@ -0,0 +1,55 @@ +from django.core.checks import Warning +from django.core.checks.mail import check_mailers_default_alias +from django.test import SimpleTestCase, override_settings + + +class MailersDefaultAliasCheckTests(SimpleTestCase): + def test_mailers_not_defined(self): + self.assertEqual(check_mailers_default_alias(None), []) + + @override_settings( + MAILERS={ + "default": { + "BACKEND": "django.core.mail.backends.smtp.EmailBackend", + } + } + ) + def test_mailers_default_alias_configured(self): + self.assertEqual(check_mailers_default_alias(None), []) + + @override_settings(MAILERS={}) + def test_mailers_empty(self): + self.assertEqual( + check_mailers_default_alias(None), + [ + Warning( + "Your MAILERS setting has no 'default' entry. Sending email " + "without a valid mailer will fail.", + hint="Add a 'default' entry to MAILERS.", + id="mail.W001", + ) + ], + ) + + @override_settings( + MAILERS={ + "secondary": { + "BACKEND": "django.core.mail.backends.smtp.EmailBackend", + } + } + ) + def test_mailers_without_default_alias(self): + self.assertEqual( + check_mailers_default_alias(None), + [ + Warning( + "Your MAILERS setting has no 'default' entry. Sending email " + "without a valid mailer will fail.", + hint=( + "Add a 'default' entry to MAILERS, or pass 'using' when " + "sending email." + ), + id="mail.W001", + ) + ], + )