diff --git a/ietf/admin/__init__.py b/ietf/admin/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/ietf/admin/apps.py b/ietf/admin/apps.py
new file mode 100644
index 0000000000..20b762cfec
--- /dev/null
+++ b/ietf/admin/apps.py
@@ -0,0 +1,6 @@
+# Copyright The IETF Trust 2024, All Rights Reserved
+from django.contrib.admin import apps as admin_apps
+
+
+class AdminConfig(admin_apps.AdminConfig):
+ default_site = "ietf.admin.sites.AdminSite"
diff --git a/ietf/admin/sites.py b/ietf/admin/sites.py
new file mode 100644
index 0000000000..69cb62ae20
--- /dev/null
+++ b/ietf/admin/sites.py
@@ -0,0 +1,15 @@
+# Copyright The IETF Trust 2024, All Rights Reserved
+from django.contrib.admin import AdminSite as _AdminSite
+from django.conf import settings
+from django.utils.safestring import mark_safe
+
+
+class AdminSite(_AdminSite):
+ site_title = "Datatracker admin"
+
+ @staticmethod
+ def site_header():
+ if settings.SERVER_MODE == "production":
+ return "Datatracker administration"
+ else:
+ return mark_safe('Datatracker administration δ')
diff --git a/ietf/api/__init__.py b/ietf/api/__init__.py
index 54b4b7424b..81f370121f 100644
--- a/ietf/api/__init__.py
+++ b/ietf/api/__init__.py
@@ -7,8 +7,10 @@
from urllib.parse import urlencode
-from django.conf import settings
+from django.apps import apps as django_apps
from django.core.exceptions import ObjectDoesNotExist
+from django.utils.module_loading import autodiscover_modules
+
import debug # pyflakes:ignore
@@ -19,40 +21,29 @@
from tastypie.exceptions import ApiFieldError
from tastypie.fields import ApiField
+
_api_list = []
-for _app in settings.INSTALLED_APPS:
- _module_dict = globals()
- if '.' in _app:
- _root, _name = _app.split('.', 1)
- if _root == 'ietf':
- if not '.' in _name:
- _api = Api(api_name=_name)
- _module_dict[_name] = _api
- _api_list.append((_name, _api))
+
+def populate_api_list():
+ for app_config in django_apps.get_app_configs():
+ _module_dict = globals()
+ if '.' in app_config.name:
+ _root, _name = app_config.name.split('.', 1)
+ if _root == 'ietf':
+ if not '.' in _name:
+ _api = Api(api_name=_name)
+ _module_dict[_name] = _api
+ _api_list.append((_name, _api))
def autodiscover():
"""
Auto-discover INSTALLED_APPS resources.py modules and fail silently when
- not present. This forces an import on them to register any admin bits they
+ not present. This forces an import on them to register any resources they
may want.
"""
+ autodiscover_modules("resources")
- from importlib import import_module
- from django.conf import settings
- from django.utils.module_loading import module_has_submodule
-
- for app in settings.INSTALLED_APPS:
- mod = import_module(app)
- # Attempt to import the app's admin module.
- try:
- import_module('%s.resources' % (app, ))
- except:
- # Decide whether to bubble up this error. If the app just
- # doesn't have an admin module, we can ignore the error
- # attempting to import it, otherwise we want it to bubble up.
- if module_has_submodule(mod, "resources"):
- raise
class ModelResource(tastypie.resources.ModelResource):
def generate_cache_key(self, *args, **kwargs):
diff --git a/ietf/api/__init__.pyi b/ietf/api/__init__.pyi
index 63d9bc513b..ededea90a7 100644
--- a/ietf/api/__init__.pyi
+++ b/ietf/api/__init__.pyi
@@ -30,4 +30,5 @@ class Serializer(): ...
class ToOneField(tastypie.fields.ToOneField): ...
class TimedeltaField(tastypie.fields.ApiField): ...
+def populate_api_list() -> None: ...
def autodiscover() -> None: ...
diff --git a/ietf/api/apps.py b/ietf/api/apps.py
new file mode 100644
index 0000000000..7eca094a62
--- /dev/null
+++ b/ietf/api/apps.py
@@ -0,0 +1,15 @@
+from django.apps import AppConfig
+from . import populate_api_list
+
+
+class ApiConfig(AppConfig):
+ name = "ietf.api"
+
+ def ready(self):
+ """Hook to do init after the app registry is fully populated
+
+ Importing models or accessing the app registry is ok here, but do not
+ interact with the database. See
+ https://docs.djangoproject.com/en/4.2/ref/applications/#django.apps.AppConfig.ready
+ """
+ populate_api_list()
diff --git a/ietf/api/urls.py b/ietf/api/urls.py
index fb2184a3f0..3c0fb872c9 100644
--- a/ietf/api/urls.py
+++ b/ietf/api/urls.py
@@ -11,6 +11,7 @@
from ietf.submit import views as submit_views
from ietf.utils.urls import url
+
api.autodiscover()
urlpatterns = [
diff --git a/ietf/settings.py b/ietf/settings.py
index 13b7506673..7572b15213 100644
--- a/ietf/settings.py
+++ b/ietf/settings.py
@@ -436,7 +436,7 @@ def skip_unreadable_post(record):
INSTALLED_APPS = [
# Django apps
- 'django.contrib.admin',
+ 'ietf.admin', # replaces django.contrib.admin
'django.contrib.admindocs',
'django.contrib.auth',
'django.contrib.contenttypes',
diff --git a/ietf/templates/admin/base.html b/ietf/templates/admin/base.html
new file mode 100644
index 0000000000..9ca7377a54
--- /dev/null
+++ b/ietf/templates/admin/base.html
@@ -0,0 +1,27 @@
+{% extends 'admin/base.html' %}
+{% load static %}
+{% block extrastyle %}{{ block.super }}
+ {% if server_mode and server_mode != "production" %}
+
+ {% endif %}
+{% endblock %}
diff --git a/ietf/urls.py b/ietf/urls.py
index 4b29a3aa81..90b161b530 100644
--- a/ietf/urls.py
+++ b/ietf/urls.py
@@ -20,8 +20,6 @@
from ietf.utils.urls import url
-admin.autodiscover()
-
# sometimes, this code gets called more than once, which is an
# that seems impossible to work around.
try:
diff --git a/ietf/utils/tests.py b/ietf/utils/tests.py
index 476c257a38..d435583e89 100644
--- a/ietf/utils/tests.py
+++ b/ietf/utils/tests.py
@@ -36,6 +36,7 @@
import debug # pyflakes:ignore
+from ietf.admin.sites import AdminSite
from ietf.person.name import name_parts, unidecode_name
from ietf.submit.tests import submission_file
from ietf.utils.draft import PlaintextDraft, getmeta
@@ -325,7 +326,7 @@ def test_all_model_admins_exist(self):
User.objects.create_superuser('admin', 'admin@example.org', 'admin+password')
self.client.login(username='admin', password='admin+password')
rtop = self.client.get("/admin/")
- self.assertContains(rtop, 'Django administration')
+ self.assertContains(rtop, AdminSite.site_header())
for name in self.apps:
app_name = self.apps[name]
self.assertContains(rtop, name)