Repository: django-wiki/django-nyt

Issue:
Testable settings pattern
As I've just discovered this, I wanna make sure to note it somewhere:

The pattern that we have for settings in django-nyt (and django-wiki) isn't perfect because it doesn't support the `@override_settings` decorator used in testing.

There are some suggestions in this Stack Overflow post: https://stackoverflow.com/questions/8428556/django-default-settings-convention-for-pluggable-app

I haven't quite understood which is the perfect pattern yet. One of the reasons to have a local `settings` module would be to easily gather an overview and at the same time document settings. Let's see if we can keep this.

Discussion:

Patch:
diff --git a/django_nyt/admin.py b/django_nyt/admin.py
index 14edf24..a4e2f1e 100644
--- a/django_nyt/admin.py
+++ b/django_nyt/admin.py
@@ -2,7 +2,7 @@ from django.contrib import admin
 from django.utils.translation import gettext as _
 
 from django_nyt import models
-from django_nyt import settings
+from django_nyt.conf import app_settings
 
 
 class SettingsAdmin(admin.ModelAdmin):
@@ -37,7 +37,7 @@ class NotificationAdmin(admin.ModelAdmin):
     raw_id_fields = ("user", "subscription")
 
 
-if settings.ENABLE_ADMIN:
+if app_settings.NYT_ENABLE_ADMIN:
     admin.site.register(models.NotificationType)
     admin.site.register(models.Notification, NotificationAdmin)
     admin.site.register(models.Settings, SettingsAdmin)
diff --git a/django_nyt/conf.py b/django_nyt/conf.py
new file mode 100644
index 0000000..770054a
--- /dev/null
+++ b/django_nyt/conf.py
@@ -0,0 +1,149 @@
+"""
+These are the available settings, accessed through ``django_nyt.conf.app_settings``.
+All attributes prefixed ``NYT_*`` can be overridden from your Django project's settings module by defining a setting with the same name.
+
+For instance, to enable the admin, add the following to your project settings:
+
+.. code-block:: python
+
+    NYT_ENABLE_ADMIN = True
+"""
+from __future__ import annotations
+
+from collections import OrderedDict
+from dataclasses import dataclass
+from dataclasses import field
+from typing import Any
+
+from django.conf import settings as django_settings
+from django.utils.translation import gettext_lazy as _
+
+# All attributes accessed with this prefix are possible to overwrite
+# through django.conf.settings.
+settings_prefix = "NYT_"
+
+INSTANTLY = 0
+# Subtract 1, because the job finishes less than 24h before the next...
+DAILY = (24 - 1) * 60
+WEEKLY = 7 * (24 - 1) * 60
+
+
+@dataclass(frozen=True)
+class AppSettings:
+    """Access this instance as ``django_nyt.conf.app_settings``."""
+
+    NYT_DB_TABLE_PREFIX: str = "nyt"
+    """The table prefix for tables in the database. Do not change this unless you know what you are doing."""
+
+    NYT_ENABLED: bool = True
+    """Global setting to force-fully disable all propagation and creation of
+    notifications."""
+
+    NYT_ENABLE_ADMIN: bool = False
+    """Enable django-admin registration for django-nyt's ModelAdmin classes."""
+
+    NYT_SEND_EMAILS: bool = True
+    """Email notifications global setting, can be used to globally switch off
+    emails, both instant and scheduled digests.
+    Remember that emails are sent with ``python manage.py notifymail``."""
+
+    NYT_EMAIL_SUBJECT: str = None
+    """Hard-code a subject for all emails sent (overrides the default subject templates)."""
+
+    NYT_EMAIL_SENDER: str = "notifications@example.com"
+    """Default sender email for notification emails. You should definitely make this match
+    an email address that your email gateway will allow you to send from. You may also
+    consider a no-reply kind of email if your notification system has a UI for changing
+    notification settings."""
+
+    NYT_EMAIL_TEMPLATE_DEFAULT: str = "notifications/emails/default.txt"
+    """Default template used for rendering email contents.
+    Should contain a valid template name.
+    If a lookup in ``NYT_EMAIL_TEMPLATE_NAMES`` doesn't return a result, this fallback is used."""
+
+    NYT_EMAIL_SUBJECT_TEMPLATE_DEFAULT: str = "notifications/emails/default_subject.txt"
+    """Default template used for rendering the email subject.
+    Should contain a valid template name.
+    If a lookup in ``NYT_EMAIL_SUBJECT_TEMPLATE_NAMES`` doesn't return a result, this fallback is used."""
+
+    NYT_EMAIL_TEMPLATE_NAMES: dict = field(default_factory=OrderedDict)
+    """Default dictionary, mapping notification keys to template names. Can be overwritten by database values.
+    Keys can have a glob pattern, like ``USER_*`` or ``user/*``.
+
+    When notification emails are generated,
+    they are grouped by their templates such that notifications sharing the same template can be sent in a combined email.
+
+    Example:
+
+    .. code-block:: python
+
+        NYT_EMAIL_TEMPLATE_NAMES = OrderedDict(
+            [
+                ("admin/product/created", "myapp/notifications/email/admin_product_added.txt"),
+                ("admin/*", "myapp/notifications/email/admin_default.txt"),
+            ]
+        )
+    """
+
+    NYT_EMAIL_SUBJECT_TEMPLATE_NAMES: dict = field(default_factory=OrderedDict)
+    """Default dictionary, mapping notification keys to template names. The templates are used to generate a single-line email subject.
+    Can be overwritten by database values.
+    Keys can have a glob pattern, like ``USER_*`` or ``user/*``.
+
+    When notification emails are generated,
+    they are grouped by their templates such that notifications sharing the same template can be sent in a combined email.
+
+    Example:
+
+    .. code-block:: python
+
+        NYT_EMAIL_SUBJECT_TEMPLATE_NAMES = OrderedDict(
+            [
+                ("admin/product/created", "myapp/notifications/email/admin_product_added.txt"),
+                ("admin/*", "myapp/notifications/email/admin_default.txt"),
+            ]
+        )
+    """
+
+    NYT_INTERVALS: list[tuple[int, Any]] | tuple[tuple[int, Any]] = (
+        (INSTANTLY, _("instantly")),
+        (DAILY, _("daily")),
+        (WEEKLY, _("weekly")),
+    )
+    """List of intervals available for user selections. In minutes"""
+
+    NYT_INTERVALS_DEFAULT: int = INSTANTLY
+    """Default selection for new subscriptions"""
+
+    NYT_USER_MODEL: str = getattr(django_settings, "AUTH_USER_MODEL", "auth.User")
+    """The swappable user model of Django Nyt. The default is to use the contents of ``AUTH_USER_MODEL``."""
+
+    ############
+    # CHANNELS #
+    ############
+
+    NYT_ENABLE_CHANNELS: str = (
+        "channels" in django_settings.INSTALLED_APPS
+        and not getattr(django_settings, "NYT_CHANNELS_DISABLE", False)
+    )
+    """Channels are enabled automatically when 'channels' application is installed,
+    however you can explicitly disable it with NYT_CHANNELS_DISABLE."""
+
+    # Name of the global channel (preliminary stuff) that alerts everyone that there
+    # is a new notification
+    NYT_NOTIFICATION_CHANNEL: str = "nyt_all-{notification_key:s}"
+
+    def __getattribute__(self, __name: str) -> Any:
+        """
+        Check if a Django project settings should override the app default.
+
+        In order to avoid returning any random properties of the django settings, we inspect the prefix firstly.
+        """
+
+        if __name.startswith(settings_prefix) and hasattr(django_settings, __name):
+            return getattr(django_settings, __name)
+
+        return super().__getattribute__(__name)
+
+
+app_settings = AppSettings()
diff --git a/django_nyt/consumers.py b/django_nyt/consumers.py
index 7ac2a5e..734a3ca 100644
--- a/django_nyt/consumers.py
+++ b/django_nyt/consumers.py
@@ -5,7 +5,7 @@ from channels.auth import channel_session_user
 from channels.auth import channel_session_user_from_http
 
 from . import models
-from . import settings
+from .conf import app_settings
 
 logger = logging.getLogger(__name__)
 
@@ -30,7 +30,7 @@ def ws_connect(message):
 
     for subscription in get_subscriptions(message):
         Group(
-            settings.NOTIFICATION_CHANNEL.format(
+            app_settings.NOTIFICATION_CHANNEL.format(
                 notification_key=subscription.notification_type.key
             )
         ).add(message.reply_channel)
@@ -44,7 +44,7 @@ def ws_disconnect(message):
     logger.debug("Removing connection for user {} (disconnect)".format(message.user))
     for subscription in get_subscriptions(message):
         Group(
-            settings.NOTIFICATION_CHANNEL.format(
+            app_settings.NOTIFICATION_CHANNEL.format(
                 notification_key=subscription.notification_type.key
             )
         ).discard(message.reply_channel)
diff --git a/django_nyt/management/commands/notifymail.py b/django_nyt/management/commands/notifymail.py
index 4a165f1..b15fff1 100644
--- a/django_nyt/management/commands/notifymail.py
+++ b/django_nyt/management/commands/notifymail.py
@@ -14,7 +14,7 @@ from django.utils.translation import activate
 from django.utils.translation import deactivate
 
 from django_nyt import models
-from django_nyt import settings as nyt_settings
+from django_nyt.conf import app_settings
 
 # Daemon / mail loop sleep between each database poll (seconds)
 SLEEP_TIME = 120
@@ -76,9 +76,9 @@ class Command(BaseCommand):
     ):
 
         # This setting overrides everything
-        if nyt_settings.EMAIL_SUBJECT:
+        if app_settings.NYT_EMAIL_SUBJECT:
             # Notice that this usually is a lazy translation object
-            subject = str(nyt_settings.EMAIL_SUBJECT)
+            subject = str(app_settings.NYT_EMAIL_SUBJECT)
         else:
             subject = render_to_string(template_subject_name, context).strip()
 
@@ -86,7 +86,7 @@ class Command(BaseCommand):
         email = mail.EmailMessage(
             subject,
             message,
-            nyt_settings.EMAIL_SENDER,
+            app_settings.NYT_EMAIL_SENDER,
             [context["user"].email],
             connection=connection,
         )
@@ -135,7 +135,7 @@ class Command(BaseCommand):
 
         self.logger.info("Starting django_nyt e-mail dispatcher")
 
-        if not nyt_settings.SEND_EMAILS:
+        if not app_settings.NYT_SEND_EMAILS:
             print("E-mails disabled - quitting.")
             sys.exit()
 
@@ -185,7 +185,7 @@ class Command(BaseCommand):
             elapsed_seconds = (last_sent - started_sending_at).seconds
             time.sleep(
                 max(
-                    (min(nyt_settings.INTERVALS)[0] - elapsed_seconds) * 60,
+                    (min(app_settings.NYT_INTERVALS)[0] - elapsed_seconds) * 60,
                     sleep_time,
                     0,
                 )
@@ -271,8 +271,8 @@ class Command(BaseCommand):
             context["username"] = getattr(setting.user, setting.user.USERNAME_FIELD)
             # get the index of the tuple corresponding to the interval and
             # get the string name
-            idx = [y[0] for y in nyt_settings.INTERVALS].index(setting.interval)
-            context["digest"] = nyt_settings.INTERVALS[idx][1]
+            idx = [y[0] for y in app_settings.NYT_INTERVALS].index(setting.interval)
+            context["digest"] = app_settings.NYT_INTERVALS[idx][1]
 
             emails_per_template = {}
 
diff --git a/django_nyt/models.py b/django_nyt/models.py
index 416bd89..5378591 100644
--- a/django_nyt/models.py
+++ b/django_nyt/models.py
@@ -9,7 +9,7 @@ from django.db.models.signals import post_save
 from django.dispatch import receiver
 from django.utils.translation import gettext_lazy as _
 
-from django_nyt import settings
+from django_nyt.conf import app_settings
 
 _notification_type_cache = {}
 
@@ -37,7 +37,7 @@ class NotificationType(models.Model):
         return self.key
 
     class Meta:
-        db_table = settings.DB_TABLE_PREFIX + "_notificationtype"
+        db_table = app_settings.NYT_DB_TABLE_PREFIX + "_notificationtype"
         verbose_name = _("type")
         verbose_name_plural = _("types")
 
@@ -53,16 +53,19 @@ class NotificationType(models.Model):
         return nt
 
     def get_email_template_name(self):
-        for key_glob, template_name in settings.EMAIL_TEMPLATE_NAMES.items():
+        for key_glob, template_name in app_settings.NYT_EMAIL_TEMPLATE_NAMES.items():
             if PurePath(self.key).match(key_glob):
                 return template_name
-        return settings.EMAIL_TEMPLATE_DEFAULT
+        return app_settings.NYT_EMAIL_TEMPLATE_DEFAULT
 
     def get_email_subject_template_name(self):
-        for key_glob, template_name in settings.EMAIL_SUBJECT_TEMPLATE_NAMES.items():
+        for (
+            key_glob,
+            template_name,
+        ) in app_settings.NYT_EMAIL_SUBJECT_TEMPLATE_NAMES.items():
             if PurePath(self.key).match(key_glob):
                 return template_name
-        return settings.EMAIL_SUBJECT_TEMPLATE_DEFAULT
+        return app_settings.NYT_EMAIL_SUBJECT_TEMPLATE_DEFAULT
 
 
 @receiver([post_save, post_delete], sender=NotificationType)
@@ -77,16 +80,16 @@ class Settings(models.Model):
     """
 
     user = models.ForeignKey(
-        settings.USER_MODEL,
+        app_settings.NYT_USER_MODEL,
         on_delete=models.CASCADE,  # If a user is deleted, remove all settings
         verbose_name=_("user"),
         related_name="nyt_settings",
     )
 
     interval = models.SmallIntegerField(
-        choices=settings.INTERVALS,
+        choices=app_settings.NYT_INTERVALS,
         verbose_name=_("interval"),
-        default=settings.INTERVALS_DEFAULT,
+        default=app_settings.NYT_INTERVALS_DEFAULT,
     )
 
     is_default = models.BooleanField(
@@ -99,7 +102,7 @@ class Settings(models.Model):
         return obj_name
 
     class Meta:
-        db_table = settings.DB_TABLE_PREFIX + "_settings"
+        db_table = app_settings.NYT_DB_TABLE_PREFIX + "_settings"
         verbose_name = _("settings")
         verbose_name_plural = _("settings")
 
@@ -184,7 +187,7 @@ class Subscription(models.Model):
         return obj_name
 
     class Meta:
-        db_table = settings.DB_TABLE_PREFIX + "_subscription"
+        db_table = app_settings.NYT_DB_TABLE_PREFIX + "_subscription"
         verbose_name = _("subscription")
         verbose_name_plural = _("subscriptions")
 
@@ -203,7 +206,7 @@ class Notification(models.Model):
     #: Or the user to receive the notification
     # If a user is deleted, remove all notifications (CASCADE)
     user = models.ForeignKey(
-        settings.USER_MODEL,
+        app_settings.NYT_USER_MODEL,
         null=True,
         blank=True,
         on_delete=models.CASCADE,
@@ -309,7 +312,7 @@ class Notification(models.Model):
         return "%s: %s" % (self.user, self.message)
 
     class Meta:
-        db_table = settings.DB_TABLE_PREFIX + "_notification"
+        db_table = app_settings.NYT_DB_TABLE_PREFIX + "_notification"
         verbose_name = _("notification")
         verbose_name_plural = _("notifications")
         ordering = ("-id",)
diff --git a/django_nyt/settings.py b/django_nyt/settings.py
deleted file mode 100644
index 7286882..0000000
--- a/django_nyt/settings.py
+++ /dev/null
@@ -1,90 +0,0 @@
-from collections import OrderedDict
-
-from django.conf import settings as django_settings
-from django.utils.translation import gettext_lazy as _
-
-DB_TABLE_PREFIX = "nyt"
-
-ENABLED = getattr(django_settings, "NYT_ENABLED", True)
-"""Global setting to force-fully disable all propagation and creation of
-notifications."""
-
-ENABLE_ADMIN = getattr(django_settings, "NYT_ENABLE_ADMIN", False)
-"""Enable django-admin registration for django-nyt's ModelAdmin's"""
-
-SEND_EMAILS = getattr(django_settings, "NYT_SEND_EMAILS", True)
-"""Email notifications global setting, can be used to globally switch off
-emails, both instant and scheduled digests.
-Remeber that emails are sent with ``python manage.py notifymail``."""
-
-EMAIL_SUBJECT = getattr(django_settings, "NYT_EMAIL_SUBJECT", None)
-"""Hard-code a subject of all emails sent (overrides subject templates)"""
-
-EMAIL_SENDER = getattr(django_settings, "NYT_EMAIL_SENDER", "notifications@example.com")
-"""Default sender email"""
-
-EMAIL_TEMPLATE_DEFAULT = "notifications/emails/default.txt"
-EMAIL_SUBJECT_TEMPLATE_DEFAULT = "notifications/emails/default_subject.txt"
-
-EMAIL_TEMPLATE_NAMES = getattr(
-    django_settings, "NYT_EMAIL_TEMPLATE_NAMES", OrderedDict()
-)
-"""Default dictionary, mapping notification keys to template names. Can be overwritten by database values.
-Keys can have a glob pattern, like USER_CHANGED_*."""
-
-EMAIL_SUBJECT_TEMPLATE_NAMES = getattr(
-    django_settings, "NYT_EMAIL_SUBJECT_TEMPLATE_NAMES", OrderedDict()
-)
-"""Default dictionary, mapping notification keys to template names. The templates are used to generate a single-line email subject.
-Can be overwritten by database values.
-Keys can have a glob pattern, like USER_CHANGED_*."""
-
-ENABLE_CHANNELS = "channels" in django_settings.INSTALLED_APPS and not getattr(
-    django_settings, "NYT_CHANNELS_DISABLE", False
-)
-
-# You can always make up more numbers... they simply identify which notifications
-# to send when invoking the script, and the number indicates how many hours
-# to minimum pass between each notification.
-INSTANTLY = 0
-# Subtract 1, because the job finishes less than 24h before the next...
-DAILY = (24 - 1) * 60
-WEEKLY = 7 * (24 - 1) * 60
-
-INTERVALS = getattr(
-    django_settings,
-    "NYT_INTERVALS",
-    [(INSTANTLY, _("instantly")), (DAILY, _("daily")), (WEEKLY, _("weekly"))],
-)
-"""List of intervals available for user selections. In minutes"""
-
-INTERVALS_DEFAULT = INSTANTLY
-"""Default selection for new subscriptions"""
-
-USER_MODEL = getattr(django_settings, "AUTH_USER_MODEL", "auth.User")
-
-
-############
-# CHANNELS #
-############
-
-ENABLE_CHANNELS = "channels" in django_settings.INSTALLED_APPS and not getattr(
-    django_settings, "NYT_CHANNELS_DISABLE", False
-)
-"""Channels are enabled automatically when 'channels' application is installed,
-however you can explicitly disable it with NYT_CHANNELS_DISABLE."""
-
-# Name of the global channel (preliminary stuff) that alerts everyone that there
-# is a new notification
-NOTIFICATION_CHANNEL = "nyt_all-{notification_key:s}"
-
-
-####################
-# PLANNED SETTINGS #
-####################
-
-# AUTO_DELETE = getattr(django_settings, 'NYT_AUTO_DELETE', 120)
-"""After how many days should viewed notifications be deleted? (not implemented)"""
-
-# AUTO_DELETE_ALL = getattr(django_settings, 'NYT_AUTO_DELETE_ALL', 120)
-"""After how many days should both viewed and unviewed notifications be deleted? (not implemented)"""
diff --git a/django_nyt/subscribers.py b/django_nyt/subscribers.py
index 36a2683..a2d287d 100644
--- a/django_nyt/subscribers.py
+++ b/django_nyt/subscribers.py
@@ -3,7 +3,7 @@ import logging
 from channels import Group
 
 from . import models
-from . import settings
+from .conf import app_settings
 
 logger = logging.getLogger(__name__)
 
@@ -21,7 +21,7 @@ def notify_subscribers(notifications, key):
 
     for notification_type in notification_type_ids:
         g = Group(
-            settings.NOTIFICATION_CHANNEL.format(
+            app_settings.NOTIFICATION_CHANNEL.format(
                 notification_key=notification_type["key"]
             )
         )
diff --git a/django_nyt/utils.py b/django_nyt/utils.py
index fc7802d..9c35359 100644
--- a/django_nyt/utils.py
+++ b/django_nyt/utils.py
@@ -3,7 +3,7 @@ from django.utils.translation import gettext as _
 
 from . import _disable_notifications
 from . import models
-from . import settings
+from .conf import app_settings
 
 
 def notify(
@@ -58,7 +58,7 @@ def notify(
     )
 
     # Notify channel subscribers if we have channels enabled
-    if settings.ENABLE_CHANNELS:
+    if app_settings.NYT_ENABLE_CHANNELS:
         from django_nyt import subscribers
 
         subscribers.notify_subscribers(objects, key)
diff --git a/docs/conf.py b/docs/conf.py
index b28c398..695b3af 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -106,11 +106,6 @@ pygments_style = "sphinx"
 
 # -- Options for HTML output ---------------------------------------------------
 
-# The theme to use for HTML and HTML Help pages.  See the documentation for
-# a list of builtin themes.
-html_theme = "default"
-
-
 on_rtd = os.environ.get("READTHEDOCS", None) == "True"
 
 
@@ -119,11 +114,10 @@ if on_rtd:
         "sphinx-apidoc --doc-project='Python Reference' -f -o . ../django_nyt ../django_nyt/tests ../django_nyt/migrations"
     )
 
-if on_rtd:
-    html_theme = "default"
-else:
-    html_theme = "sphinx_rtd_theme"
+html_theme = "sphinx_rtd_theme"
 
+# This is in order to have a simpler display for autodoc'ed pages
+add_module_names = False
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
diff --git a/docs/configuration.rst b/docs/configuration.rst
index 403290c..c53887a 100644
--- a/docs/configuration.rst
+++ b/docs/configuration.rst
@@ -1,13 +1,8 @@
 Configuration
 =============
 
-These are the settings from ``django_nyt.settings`` that can be overridden from
-your project settings.
-
-To override a setting, prefix the name of that setting with ``NYT_``. For instance,
-to override ``EMAIL_SENDER``, set the value of ``NYT_EMAIL_SENDER`` in your
-project's settings.
-
-.. automodule:: django_nyt.settings
-   :members:
+.. automodule:: django_nyt.conf
    :noindex:
+
+   .. autoclass:: AppSettings()
+      :members:
diff --git a/docs/release_notes.rst b/docs/release_notes.rst
index 01036f0..4d3fb5a 100644
--- a/docs/release_notes.rst
+++ b/docs/release_notes.rst
@@ -16,6 +16,7 @@ Release Notes
 * Tests migrated to pytest #123 (Benjamin Balder Bach)
 * Default email notification paths are changed to ``notifications/emails/default.txt`` and ``notifications/emails/default_subject.txt`` #125 (Benjamin Balder Bach)
 * Notification URLs added to emails have a hard-coded `https://` (before, this was `http://`) #125 (Benjamin Balder Bach)
+* New test-friendly settings pattern changes internal names of settings, but has no effects on Django settings #127 (Benjamin Balder Bach).
 
 **Fixed**
 


Test patch:
diff --git a/tests/core/test_email.py b/tests/core/test_email.py
index a6e5b9b..a38d6be 100644
--- a/tests/core/test_email.py
+++ b/tests/core/test_email.py
@@ -81,15 +81,6 @@ example.com
         ),
     )
     def test_custom_email_template(self):
-        # TODO: https://github.com/django-wiki/django-nyt/issues/124
-        from django_nyt import settings as nyt_settings
-
-        nyt_settings.EMAIL_TEMPLATE_NAMES = OrderedDict(
-            {NotifyTestBase.TEST_KEY: "testapp/notifications/email.txt"}
-        )
-        nyt_settings.EMAIL_SUBJECT_TEMPLATE_NAMES = OrderedDict(
-            {NotifyTestBase.TEST_KEY: "testapp/notifications/email_subject.txt"}
-        )
 
         mail.outbox = []
 
@@ -103,28 +94,22 @@ example.com
         assert mail.outbox[0].subject == "subject"
         assert mail.outbox[0].body == "Test\n"
 
-        # Reset to default state
-        # TODO: https://github.com/django-wiki/django-nyt/issues/124
-        nyt_settings.EMAIL_TEMPLATE_NAMES = OrderedDict()
-        nyt_settings.EMAIL_SUBJECT_TEMPLATE_NAMES = OrderedDict()
-
+    @override_settings(
+        NYT_EMAIL_TEMPLATE_NAMES=OrderedDict(
+            [
+                ("key1", "testapp/notifications/email.txt"),
+                ("admin_*", "testapp/notifications/admin.txt"),
+            ]
+        ),
+        NYT_EMAIL_SUBJECT_TEMPLATE_NAMES=OrderedDict(
+            [
+                ("key1", "testapp/notifications/email_subject.txt"),
+                ("admin_*", "testapp/notifications/admin_subject.txt"),
+                ("*", "notifications/emails/default_subject.txt"),
+            ]
+        ),
+    )
     def test_multiple_templates(self):
-        # TODO: https://github.com/django-wiki/django-nyt/issues/124
-        from django_nyt import settings as nyt_settings
-
-        nyt_settings.EMAIL_TEMPLATE_NAMES = OrderedDict(
-            {
-                "key1": "testapp/notifications/email.txt",
-                "admin_*": "testapp/notifications/admin.txt",
-            }
-        )
-        nyt_settings.EMAIL_SUBJECT_TEMPLATE_NAMES = OrderedDict(
-            {
-                "key1": "testapp/notifications/email_subject.txt",
-                "admin_*": "testapp/notifications/admin_subject.txt",
-                "*": "notifications/emails/default_subject.txt",
-            }
-        )
 
         # Subscribe User 1 to 3 keys
         utils.subscribe(self.user1_settings, "key1", send_emails=True)
@@ -149,20 +134,9 @@ example.com
             == "You have new notifications from example.com (type: instantly)"
         )
 
-        # Reset to default state
-        # TODO: https://github.com/django-wiki/django-nyt/issues/124
-        nyt_settings.EMAIL_TEMPLATE_NAMES = OrderedDict()
-        nyt_settings.EMAIL_SUBJECT_TEMPLATE_NAMES = OrderedDict()
-
     @override_settings(NYT_EMAIL_SUBJECT="test")
     def test_nyt_email_subject(self):
 
-        # TODO: https://github.com/django-wiki/django-nyt/issues/124
-        from django_nyt import settings as nyt_settings
-
-        old_setting = nyt_settings.EMAIL_SUBJECT
-        nyt_settings.EMAIL_SUBJECT = "test"
-
         mail.outbox = []
 
         # Subscribe User 1 to test key
@@ -173,7 +147,3 @@ example.com
 
         assert len(mail.outbox) == 1
         assert mail.outbox[0].subject == "test"
-
-        # Reset to default state
-        # TODO: https://github.com/django-wiki/django-nyt/issues/124
-        nyt_settings.EMAIL_SUBJECT = old_setting