an adminsite object encapsulates an instance of the django admin application ready to be hooked in to your urlconf models are registered with the adminsite using the register method and the geturls method can then be used to access django view functions that present a full admin interface for the collection of registered models text to put at the end of each page s title text to put in each page s div idsitename text to put at the top of the admin index page url for the view site link at the top of each admin page run the system checks on all modeladmins except if they aren t customized at all register the given models with the given admin class the models should be model classes not instances if an admin class isn t given use modeladmin the default admin options if keyword arguments are given e g listdisplay apply them as options to the admin class if a model is already registered raise alreadyregistered if a model is abstract raise improperlyconfigured most likely registered without a modeladmin subclass ignore the registration if the model has been swapped out if we got options then dynamically construct a subclass of adminclass with those options for reasons i don t quite understand without a module the created class appears to live in the wrong place which causes issues later on instantiate the admin class to save in the registry unregister the given models if a model isn t already registered raise notregistered check if a model class is registered with this adminsite register an action to be available globally disable a globallyregistered action raise keyerror for invalid names explicitly get a registered global action whether it s enabled or not raise keyerror for invalid names get all the enabled actions as an iterable of name func return true if the given httprequest has permission to view at least one page in the admin site decorator to create an admin view attached to this adminsite this wraps the view and provides permission checking by calling self haspermission you ll want to use this from within adminsite geturls class myadminsiteadminsite def geturlsself from django urls import path urls super geturls urls path myview self adminviewsomeview return urls by default adminviews are marked noncacheable using the nevercache decorator if the view can be safely cached set cacheabletrue inner import to prevent django contrib admin app from importing django contrib auth models user unrelated model we add csrfprotect here so this function can be used as a utility function for any view without having to repeat csrfprotect since this module gets imported in the application s root package it cannot import models from other applications at the module level and django contrib contenttypes views imports contenttype adminsitewide views add in each model s views and create a list of valid urls for the appindex if there were modeladmins registered we should have a list of app labels for which we need to allow access to the appindex view return a dictionary of variables to put in the template context for every page in the admin site for sites running on a subpath use the scriptname value if siteurl hasn t been customized handle the change password task both form display and validation display the success page after a password change display the i18n javascript that the django admin requires extracontext is unused but present for consistency with the other admin views log out the user for the given httprequest this should not assume the user is already logged in since the user isn t logged out at this point the value of haspermission must be overridden display the login form for the given httprequest already loggedin redirect to admin index since this module gets imported in the application s root package it cannot import models from other applications at the module level and django contrib admin forms eventually imports user build the app dictionary the optional label parameter filters models of a specific app check whether user has any perm for this module if so add the module to the modellist return a sorted list of all the installed apps that have been registered in this site sort the apps alphabetically sort the models alphabetically within each app display the main admin index page which lists all of the installed apps that have been registered in this site this global object represents the default admin site for the common case you can provide your own adminsite using the simpleadminconfig defaultsite attribute you can also instantiate adminsite in your own code to create a custom admin site an adminsite object encapsulates an instance of the django admin application ready to be hooked in to your urlconf models are registered with the adminsite using the register method and the get_urls method can then be used to access django view functions that present a full admin interface for the collection of registered models text to put at the end of each page s title text to put in each page s div id site name text to put at the top of the admin index page url for the view site link at the top of each admin page model_class class admin_class instance run the system checks on all modeladmins except if they aren t customized at all speed up lookups below register the given model s with the given admin class the model s should be model classes not instances if an admin class isn t given use modeladmin the default admin options if keyword arguments are given e g list_display apply them as options to the admin class if a model is already registered raise alreadyregistered if a model is abstract raise improperlyconfigured most likely registered without a modeladmin subclass ignore the registration if the model has been swapped out if we got options then dynamically construct a subclass of admin_class with those options for reasons i don t quite understand without a __module__ the created class appears to live in the wrong place which causes issues later on instantiate the admin class to save in the registry unregister the given model s if a model isn t already registered raise notregistered check if a model class is registered with this adminsite register an action to be available globally disable a globally registered action raise keyerror for invalid names explicitly get a registered global action whether it s enabled or not raise keyerror for invalid names get all the enabled actions as an iterable of name func return true if the given httprequest has permission to view at least one page in the admin site decorator to create an admin view attached to this adminsite this wraps the view and provides permission checking by calling self has_permission you ll want to use this from within adminsite get_urls class myadminsite adminsite def get_urls self from django urls import path urls super get_urls urls path my_view self admin_view some_view return urls by default admin_views are marked non cacheable using the never_cache decorator if the view can be safely cached set cacheable true inner import to prevent django contrib admin app from importing django contrib auth models user unrelated model we add csrf_protect here so this function can be used as a utility function for any view without having to repeat csrf_protect since this module gets imported in the application s root package it cannot import models from other applications at the module level and django contrib contenttypes views imports contenttype admin site wide views add in each model s views and create a list of valid urls for the app_index if there were modeladmins registered we should have a list of app labels for which we need to allow access to the app_index view return a dictionary of variables to put in the template context for every page in the admin site for sites running on a subpath use the script_name value if site_url hasn t been customized handle the change password task both form display and validation display the success page after a password change display the i18n javascript that the django admin requires extra_context is unused but present for consistency with the other admin views log out the user for the given httprequest this should not assume the user is already logged in since the user isn t logged out at this point the value of has_permission must be overridden display the login form for the given httprequest already logged in redirect to admin index since this module gets imported in the application s root package it cannot import models from other applications at the module level and django contrib admin forms eventually imports user build the app dictionary the optional label parameter filters models of a specific app check whether user has any perm for this module if so add the module to the model_list return a sorted list of all the installed apps that have been registered in this site sort the apps alphabetically sort the models alphabetically within each app display the main admin index page which lists all of the installed apps that have been registered in this site this global object represents the default admin site for the common case you can provide your own adminsite using the simple adminconfig default_site attribute you can also instantiate adminsite in your own code to create a custom admin site
from functools import update_wrapper
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
from django.db.models.base import ModelBase
from django.http import Http404, HttpResponsePermanentRedirect, HttpResponseRedirect
from django.template.response import TemplateResponse
from django.urls import NoReverseMatch, Resolver404, resolve, reverse
from django.utils.decorators import method_decorator
from django.utils.functional import LazyObject
from django.utils.module_loading import import_string
from django.utils.text import capfirst
from django.utils.translation import gettext as _
from django.utils.translation import gettext_lazy
from django.views.decorators.cache import never_cache
from django.views.decorators.common import no_append_slash
from django.views.decorators.csrf import csrf_protect
from django.views.i18n import JavaScriptCatalog

all_sites = WeakSet()


class AdminSite:
    

    
    site_title = gettext_lazy("Django site admin")

    
    site_header = gettext_lazy("Django administration")

    
    index_title = gettext_lazy("Site administration")

    
    site_url = "/"

    enable_nav_sidebar = True

    empty_value_display = "-"

    login_form = None
    index_template = None
    app_index_template = None
    login_template = None
    logout_template = None
    password_change_template = None
    password_change_done_template = None

    final_catch_all_view = True

    def __init__(self, name="admin"):
        self._registry = {}  
        self.name = name
        self._actions = {"delete_selected": actions.delete_selected}
        self._global_actions = self._actions.copy()
        all_sites.add(self)

    def __repr__(self):
        return f"{self.__class__.__name__}(name={self.name!r})"

    def check(self, app_configs):
        
        if app_configs is None:
            app_configs = apps.get_app_configs()
        app_configs = set(app_configs)  

        errors = []
        modeladmins = (
            o for o in self._registry.values() if o.__class__ is not ModelAdmin
        )
        for modeladmin in modeladmins:
            if modeladmin.model._meta.app_config in app_configs:
                errors.extend(modeladmin.check())
        return errors

    def register(self, model_or_iterable, admin_class=None, **options):
        
        admin_class = admin_class or ModelAdmin
        if isinstance(model_or_iterable, ModelBase):
            model_or_iterable = [model_or_iterable]
        for model in model_or_iterable:
            if model._meta.abstract:
                raise ImproperlyConfigured(
                    "The model %s is abstract, so it cannot be registered with admin."
                    % model.__name__
                )

            if self.is_registered(model):
                registered_admin = str(self.get_model_admin(model))
                msg = "The model %s is already registered " % model.__name__
                if registered_admin.endswith(".ModelAdmin"):
                    
                    msg += "in app %r." % registered_admin.removesuffix(".ModelAdmin")
                else:
                    msg += "with %r." % registered_admin
                raise AlreadyRegistered(msg)

            
            
            if not model._meta.swapped:
                
                
                if options:
                    
                    
                    
                    options["__module__"] = __name__
                    admin_class = type(
                        "%sAdmin" % model.__name__, (admin_class,), options
                    )

                
                self._registry[model] = admin_class(model, self)

    def unregister(self, model_or_iterable):
        
        if isinstance(model_or_iterable, ModelBase):
            model_or_iterable = [model_or_iterable]
        for model in model_or_iterable:
            if not self.is_registered(model):
                raise NotRegistered("The model %s is not registered" % model.__name__)
            del self._registry[model]

    def is_registered(self, model):
        
        return model in self._registry

    def get_model_admin(self, model):
        try:
            return self._registry[model]
        except KeyError:
            raise NotRegistered(f"The model {model.__name__} is not registered.")

    def add_action(self, action, name=None):
        
        name = name or action.__name__
        self._actions[name] = action
        self._global_actions[name] = action

    def disable_action(self, name):
        
        del self._actions[name]

    def get_action(self, name):
        
        return self._global_actions[name]

    @property
    def actions(self):
        
        return self._actions.items()

    def has_permission(self, request):
        
        return request.user.is_active and request.user.is_staff

    def admin_view(self, view, cacheable=False):
        

        def inner(request, *args, **kwargs):
            if not self.has_permission(request):
                if request.path == reverse("admin:logout", current_app=self.name):
                    index_path = reverse("admin:index", current_app=self.name)
                    return HttpResponseRedirect(index_path)
                
                
                from django.contrib.auth.views import redirect_to_login

                return redirect_to_login(
                    request.get_full_path(),
                    reverse("admin:login", current_app=self.name),
                )
            return view(request, *args, **kwargs)

        if not cacheable:
            inner = never_cache(inner)
        
        
        if not getattr(view, "csrf_exempt", False):
            inner = csrf_protect(inner)
        return update_wrapper(inner, view)

    def get_urls(self):
        
        
        
        from django.contrib.contenttypes import views as contenttype_views
        from django.urls import include, path, re_path

        def wrap(view, cacheable=False):
            def wrapper(*args, **kwargs):
                return self.admin_view(view, cacheable)(*args, **kwargs)

            wrapper.admin_site = self
            return update_wrapper(wrapper, view)

        
        urlpatterns = [
            path("", wrap(self.index), name="index"),
            path("login/", self.login, name="login"),
            path("logout/", wrap(self.logout), name="logout"),
            path(
                "password_change/",
                wrap(self.password_change, cacheable=True),
                name="password_change",
            ),
            path(
                "password_change/done/",
                wrap(self.password_change_done, cacheable=True),
                name="password_change_done",
            ),
            path("autocomplete/", wrap(self.autocomplete_view), name="autocomplete"),
            path("jsi18n/", wrap(self.i18n_javascript, cacheable=True), name="jsi18n"),
            path(
                "r/<int:content_type_id>/<path:object_id>/",
                wrap(contenttype_views.shortcut),
                name="view_on_site",
            ),
        ]

        
        
        valid_app_labels = []
        for model, model_admin in self._registry.items():
            urlpatterns += [
                path(
                    "%s/%s/" % (model._meta.app_label, model._meta.model_name),
                    include(model_admin.urls),
                ),
            ]
            if model._meta.app_label not in valid_app_labels:
                valid_app_labels.append(model._meta.app_label)

        
        
        if valid_app_labels:
            regex = r"^(?P<app_label>" + "|".join(valid_app_labels) + ")/$"
            urlpatterns += [
                re_path(regex, wrap(self.app_index), name="app_list"),
            ]

        if self.final_catch_all_view:
            urlpatterns.append(re_path(r"(?P<url>.*)$", wrap(self.catch_all_view)))

        return urlpatterns

    @property
    def urls(self):
        return self.get_urls(), "admin", self.name

    def each_context(self, request):
        
        script_name = request.META["SCRIPT_NAME"]
        site_url = (
            script_name if self.site_url == "/" and script_name else self.site_url
        )
        return {
            "site_title": self.site_title,
            "site_header": self.site_header,
            "site_url": site_url,
            "has_permission": self.has_permission(request),
            "available_apps": self.get_app_list(request),
            "is_popup": False,
            "is_nav_sidebar_enabled": self.enable_nav_sidebar,
            "log_entries": self.get_log_entries(request),
        }

    def password_change(self, request, extra_context=None):
        
        from django.contrib.admin.forms import AdminPasswordChangeForm
        from django.contrib.auth.views import PasswordChangeView

        url = reverse("admin:password_change_done", current_app=self.name)
        defaults = {
            "form_class": AdminPasswordChangeForm,
            "success_url": url,
            "extra_context": {**self.each_context(request), **(extra_context or {})},
        }
        if self.password_change_template is not None:
            defaults["template_name"] = self.password_change_template
        request.current_app = self.name
        return PasswordChangeView.as_view(**defaults)(request)

    def password_change_done(self, request, extra_context=None):
        
        from django.contrib.auth.views import PasswordChangeDoneView

        defaults = {
            "extra_context": {**self.each_context(request), **(extra_context or {})},
        }
        if self.password_change_done_template is not None:
            defaults["template_name"] = self.password_change_done_template
        request.current_app = self.name
        return PasswordChangeDoneView.as_view(**defaults)(request)

    def i18n_javascript(self, request, extra_context=None):
        
        return JavaScriptCatalog.as_view(packages=["django.contrib.admin"])(request)

    def logout(self, request, extra_context=None):
        
        from django.contrib.auth.views import LogoutView

        defaults = {
            "extra_context": {
                **self.each_context(request),
                
                
                "has_permission": False,
                **(extra_context or {}),
            },
        }
        if self.logout_template is not None:
            defaults["template_name"] = self.logout_template
        request.current_app = self.name
        return LogoutView.as_view(**defaults)(request)

    @method_decorator(never_cache)
    def login(self, request, extra_context=None):
        
        if request.method == "GET" and self.has_permission(request):
            
            index_path = reverse("admin:index", current_app=self.name)
            return HttpResponseRedirect(index_path)

        
        
        
        from django.contrib.admin.forms import AdminAuthenticationForm
        from django.contrib.auth.views import LoginView

        context = {
            **self.each_context(request),
            "title": _("Log in"),
            "subtitle": None,
            "app_path": request.get_full_path(),
            "username": request.user.get_username(),
        }
        if (
            REDIRECT_FIELD_NAME not in request.GET
            and REDIRECT_FIELD_NAME not in request.POST
        ):
            context[REDIRECT_FIELD_NAME] = reverse("admin:index", current_app=self.name)
        context.update(extra_context or {})

        defaults = {
            "extra_context": context,
            "authentication_form": self.login_form or AdminAuthenticationForm,
            "template_name": self.login_template or "admin/login.html",
        }
        request.current_app = self.name
        return LoginView.as_view(**defaults)(request)

    def autocomplete_view(self, request):
        return AutocompleteJsonView.as_view(admin_site=self)(request)

    @no_append_slash
    def catch_all_view(self, request, url):
        if settings.APPEND_SLASH and not url.endswith("/"):
            urlconf = getattr(request, "urlconf", None)
            try:
                match = resolve("%s/" % request.path_info, urlconf)
            except Resolver404:
                pass
            else:
                if getattr(match.func, "should_append_slash", True):
                    return HttpResponsePermanentRedirect(
                        request.get_full_path(force_append_slash=True)
                    )
        raise Http404

    def _build_app_dict(self, request, label=None):
        
        app_dict = {}

        if label:
            models = {
                m: m_a
                for m, m_a in self._registry.items()
                if m._meta.app_label == label
            }
        else:
            models = self._registry

        for model, model_admin in models.items():
            app_label = model._meta.app_label

            has_module_perms = model_admin.has_module_permission(request)
            if not has_module_perms:
                continue

            perms = model_admin.get_model_perms(request)

            
            
            if True not in perms.values():
                continue

            info = (app_label, model._meta.model_name)
            model_dict = {
                "model": model,
                "name": capfirst(model._meta.verbose_name_plural),
                "object_name": model._meta.object_name,
                "perms": perms,
                "admin_url": None,
                "add_url": None,
            }
            if perms.get("change") or perms.get("view"):
                model_dict["view_only"] = not perms.get("change")
                try:
                    model_dict["admin_url"] = reverse(
                        "admin:%s_%s_changelist" % info, current_app=self.name
                    )
                except NoReverseMatch:
                    pass
            if perms.get("add"):
                try:
                    model_dict["add_url"] = reverse(
                        "admin:%s_%s_add" % info, current_app=self.name
                    )
                except NoReverseMatch:
                    pass

            if app_label in app_dict:
                app_dict[app_label]["models"].append(model_dict)
            else:
                app_dict[app_label] = {
                    "name": apps.get_app_config(app_label).verbose_name,
                    "app_label": app_label,
                    "app_url": reverse(
                        "admin:app_list",
                        kwargs={"app_label": app_label},
                        current_app=self.name,
                    ),
                    "has_module_perms": has_module_perms,
                    "models": [model_dict],
                }

        return app_dict

    def get_app_list(self, request, app_label=None):
        
        app_dict = self._build_app_dict(request, app_label)

        
        app_list = sorted(app_dict.values(), key=lambda x: x["name"].lower())

        
        for app in app_list:
            app["models"].sort(key=lambda x: x["name"])

        return app_list

    def index(self, request, extra_context=None):
        
        app_list = self.get_app_list(request)

        context = {
            **self.each_context(request),
            "title": self.index_title,
            "subtitle": None,
            "app_list": app_list,
            **(extra_context or {}),
        }

        request.current_app = self.name

        return TemplateResponse(
            request, self.index_template or "admin/index.html", context
        )

    def app_index(self, request, app_label, extra_context=None):
        app_list = self.get_app_list(request, app_label)

        if not app_list:
            raise Http404("The requested admin page does not exist.")

        context = {
            **self.each_context(request),
            "title": _("%(app)s administration") % {"app": app_list[0]["name"]},
            "subtitle": None,
            "app_list": app_list,
            "app_label": app_label,
            **(extra_context or {}),
        }

        request.current_app = self.name

        return TemplateResponse(
            request,
            self.app_index_template
            or ["admin/%s/app_index.html" % app_label, "admin/app_index.html"],
            context,
        )

    def get_log_entries(self, request):
        from django.contrib.admin.models import LogEntry

        return LogEntry.objects.select_related("content_type", "user")


class DefaultAdminSite(LazyObject):
    def _setup(self):
        AdminSiteClass = import_string(apps.get_app_config("admin").default_site)
        self._wrapped = AdminSiteClass()

    def __repr__(self):
        return repr(self._wrapped)






site = DefaultAdminSite()