diff --git a/django/contrib/admin/checks.py b/django/contrib/admin/checks.py index f5aa9b55d4..1665023434 100644 --- a/django/contrib/admin/checks.py +++ b/django/contrib/admin/checks.py @@ -3,6 +3,7 @@ from itertools import chain from django.apps import apps from django.conf import settings +from django.contrib.admin.exceptions import NotRegistered from django.contrib.admin.utils import NotRelationField, flatten, get_fields_from_path from django.core import checks from django.core.exceptions import FieldDoesNotExist @@ -220,8 +221,6 @@ class BaseModelAdminChecks: ManyToManyField and that the item has a related ModelAdmin with search_fields defined. """ - from django.contrib.admin.sites import NotRegistered - try: field = obj.model._meta.get_field(field_name) except FieldDoesNotExist: diff --git a/django/contrib/admin/exceptions.py b/django/contrib/admin/exceptions.py index 2ee8f625ca..6105eef424 100644 --- a/django/contrib/admin/exceptions.py +++ b/django/contrib/admin/exceptions.py @@ -11,3 +11,15 @@ class DisallowedModelAdminToField(SuspiciousOperation): """Invalid to_field was passed to admin view via URL query string""" pass + + +class AlreadyRegistered(Exception): + """The model is already registered.""" + + pass + + +class NotRegistered(Exception): + """The model is not registered.""" + + pass diff --git a/django/contrib/admin/filters.py b/django/contrib/admin/filters.py index 197d39bf21..06dedf8727 100644 --- a/django/contrib/admin/filters.py +++ b/django/contrib/admin/filters.py @@ -7,6 +7,7 @@ certain test -- e.g. being a DateField or ForeignKey. """ import datetime +from django.contrib.admin.exceptions import NotRegistered from django.contrib.admin.options import IncorrectLookupParameters from django.contrib.admin.utils import ( build_q_object_from_lookup_parameters, @@ -257,8 +258,6 @@ class RelatedFieldListFilter(FieldListFilter): """ Return the model admin's ordering for related field, if provided. """ - from django.contrib.admin.sites import NotRegistered - try: related_admin = model_admin.admin_site.get_model_admin( field.remote_field.model diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index f9760664dd..61ec0a638d 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -14,7 +14,7 @@ from django.contrib.admin.checks import ( InlineModelAdminChecks, ModelAdminChecks, ) -from django.contrib.admin.exceptions import DisallowedModelAdminToField +from django.contrib.admin.exceptions import DisallowedModelAdminToField, NotRegistered from django.contrib.admin.templatetags.admin_urls import add_preserved_filters from django.contrib.admin.utils import ( NestedObjects, @@ -160,8 +160,6 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass): If kwargs are given, they're passed to the form Field's constructor. """ - from django.contrib.admin.sites import NotRegistered - # If the field specifies choices, we don't need to look for special # admin widgets - we just need to use a select widget of some kind. if db_field.choices: @@ -252,8 +250,6 @@ class BaseModelAdmin(metaclass=forms.MediaDefiningClass): ordering. Otherwise don't specify the queryset, let the field decide (return None in that case). """ - from django.contrib.admin.sites import NotRegistered - try: related_admin = self.admin_site.get_model_admin(db_field.remote_field.model) except NotRegistered: diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py index 41ce85db79..bb02cb08ac 100644 --- a/django/contrib/admin/sites.py +++ b/django/contrib/admin/sites.py @@ -4,6 +4,7 @@ from weakref import WeakSet from django.apps import apps from django.conf import settings from django.contrib.admin import ModelAdmin, actions +from django.contrib.admin.exceptions import AlreadyRegistered, NotRegistered from django.contrib.admin.views.autocomplete import AutocompleteJsonView from django.contrib.auth import REDIRECT_FIELD_NAME from django.core.exceptions import ImproperlyConfigured @@ -25,14 +26,6 @@ from django.views.i18n import JavaScriptCatalog all_sites = WeakSet() -class AlreadyRegistered(Exception): - pass - - -class NotRegistered(Exception): - pass - - class AdminSite: """ An AdminSite object encapsulates an instance of the Django admin application, ready diff --git a/django/contrib/admin/views/autocomplete.py b/django/contrib/admin/views/autocomplete.py index 051b9a31b9..3c12c262ae 100644 --- a/django/contrib/admin/views/autocomplete.py +++ b/django/contrib/admin/views/autocomplete.py @@ -1,4 +1,5 @@ from django.apps import apps +from django.contrib.admin.exceptions import NotRegistered from django.core.exceptions import FieldDoesNotExist, PermissionDenied from django.http import Http404, JsonResponse from django.views.generic.list import BaseListView @@ -74,8 +75,6 @@ class AutocompleteJsonView(BaseListView): Raise Http404 if the target model admin is not configured properly with search_fields. """ - from django.contrib.admin.sites import NotRegistered - term = request.GET.get("term", "") try: app_label = request.GET["app_label"] diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index c8965f9b74..c329a187bb 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -3014,14 +3014,14 @@ Templates can override or extend base admin templates as described in as options to the admin class. Raises :class:`~django.core.exceptions.ImproperlyConfigured` if a model is - abstract. and ``django.contrib.admin.sites.AlreadyRegistered`` if a model - is already registered. + abstract. and ``django.contrib.admin.exceptions.AlreadyRegistered`` if a + model is already registered. .. method:: AdminSite.unregister(model_or_iterable) Unregisters the given model class (or iterable of classes). - Raises ``django.contrib.admin.sites.NotRegistered`` if a model isn't + Raises ``django.contrib.admin.exceptions.NotRegistered`` if a model isn't already registered. .. method:: AdminSite.get_model_admin(model) @@ -3029,7 +3029,7 @@ Templates can override or extend base admin templates as described in .. versionadded:: 5.0 Returns an admin class for the given model class. Raises - ``django.contrib.admin.sites.NotRegistered`` if a model isn't registered. + ``django.contrib.admin.exceptions.NotRegistered`` if a model isn't registered. .. method:: AdminSite.get_log_entries(request) diff --git a/docs/releases/5.0.txt b/docs/releases/5.0.txt index d39703888a..aa397e8cb0 100644 --- a/docs/releases/5.0.txt +++ b/docs/releases/5.0.txt @@ -487,6 +487,9 @@ Miscellaneous * The minimum supported version of ``selenium`` is increased from 3.8.0 to 4.8.0. +* The ``AlreadyRegistered`` and ``NotRegistered`` exceptions are moved from + ``django.contrib.admin.sites`` to ``django.contrib.admin.exceptions``. + .. _deprecated-features-5.0: Features deprecated in 5.0 diff --git a/tests/admin_registration/tests.py b/tests/admin_registration/tests.py index c8caf3f202..3b0e656f5f 100644 --- a/tests/admin_registration/tests.py +++ b/tests/admin_registration/tests.py @@ -1,5 +1,6 @@ from django.contrib import admin from django.contrib.admin.decorators import register +from django.contrib.admin.exceptions import AlreadyRegistered, NotRegistered from django.contrib.admin.sites import site from django.core.exceptions import ImproperlyConfigured from django.test import SimpleTestCase @@ -35,7 +36,7 @@ class TestRegistration(SimpleTestCase): def test_prevent_double_registration(self): self.site.register(Person) msg = "The model Person is already registered in app 'admin_registration'." - with self.assertRaisesMessage(admin.sites.AlreadyRegistered, msg): + with self.assertRaisesMessage(AlreadyRegistered, msg): self.site.register(Person) def test_prevent_double_registration_for_custom_admin(self): @@ -47,12 +48,12 @@ class TestRegistration(SimpleTestCase): "The model Person is already registered with " "'admin_registration.PersonAdmin'." ) - with self.assertRaisesMessage(admin.sites.AlreadyRegistered, msg): + with self.assertRaisesMessage(AlreadyRegistered, msg): self.site.register(Person, PersonAdmin) def test_unregister_unregistered_model(self): msg = "The model Person is not registered" - with self.assertRaisesMessage(admin.sites.NotRegistered, msg): + with self.assertRaisesMessage(NotRegistered, msg): self.site.unregister(Person) def test_registration_with_star_star_options(self): @@ -61,7 +62,7 @@ class TestRegistration(SimpleTestCase): def test_get_model_admin_unregister_model(self): msg = "The model Person is not registered." - with self.assertRaisesMessage(admin.sites.NotRegistered, msg): + with self.assertRaisesMessage(NotRegistered, msg): self.site.get_model_admin(Person) def test_star_star_overrides(self): diff --git a/tests/admin_views/test_autocomplete_view.py b/tests/admin_views/test_autocomplete_view.py index f8ab32717a..968f160c8e 100644 --- a/tests/admin_views/test_autocomplete_view.py +++ b/tests/admin_views/test_autocomplete_view.py @@ -3,7 +3,7 @@ import json from contextlib import contextmanager from django.contrib import admin -from django.contrib.admin.sites import NotRegistered +from django.contrib.admin.exceptions import NotRegistered from django.contrib.admin.tests import AdminSeleniumTestCase from django.contrib.admin.views.autocomplete import AutocompleteJsonView from django.contrib.auth.models import Permission, User