diff --git a/checkpoint.json b/checkpoint.json index b6640cbad..a9326de2c 100644 --- a/checkpoint.json +++ b/checkpoint.json @@ -1,20 +1,23 @@ { - "timestamp": "2026-03-16T05:35:43.163728Z", - "current_node": "solve", + "timestamp": "2026-03-16T05:35:45.415751Z", + "current_node": "extract_patch", "completed_nodes": [ "start", "setup", - "solve" + "solve", + "extract_patch" ], "node_retries": { - "start": 1, "setup": 1, + "extract_patch": 1, + "start": 1, "solve": 1 }, "context_values": { "failure_class": "", - "current_node": "solve", + "current_node": "extract_patch", "graph.rankdir": "LR", + "internal.retry_count.extract_patch": 1, "last_response": "The changes are clean and minimal. Here's a summary of what was done:\n\n1. **Renamed `_build_app_dict` to `build_app_dict`** (line 433) — Made the method public as requested, since it's used by both ", "thread.start.current_node": "setup", "graph.goal": "Added model class to app_list context\nDescription\n\t \n\t\t(last modified by Raffaele Salmaso)\n\t \nI need to manipulate the app_list in my custom admin view, and the easiest way to get the result is to have access to the model class (currently the dictionary is a serialized model).\nIn addition I would make the _build_app_dict method public, as it is used by the two views index and app_index.\n", @@ -25,17 +28,43 @@ "internal.retry_count.solve": 1, "last_stage": "solve", "response.solve": "The changes are clean and minimal. Here's a summary of what was done:\n\n1. **Renamed `_build_app_dict` to `build_app_dict`** (line 433) — Made the method public as requested, since it's used by both `index` and `app_index` views and is useful for custom admin views.\n\n2. **Added `'model': model`** to the `model_dict` dictionary (line 464) — This adds the actual model class to the app_list context, allowing custom admin views to access and manipulate it directly.\n\n3. **Updated both call sites** (lines 506 and 537) — Changed `self._build_app_dict(...)` to `self.build_app_dict(...)` in `get_app_list()` and `app_index()`.\n\nAll 416 existing admin_views tests pass with these changes.", - "internal.thread_id": "setup", - "current.preamble": "Goal: Added model class to app_list context\nDescription\n\t \n\t\t(last modified by Raffaele Salmaso)\n\t \nI need to manipulate the app_list in my custom admin view, and the easiest way to get the result is to have access to the model class (currently the dictionary is a serialized model).\nIn addition I would make the _build_app_dict method public, as it is used by the two views index and app_index.\n\n\n## Completed stages\n- **setup**: fail\n - Script: `git clone https://github.com/django/django.git . && git checkout 0456d3e42795481a186db05719300691fe2a1029 && python -m pip install -e .`\n - Stdout:\n ```\n fatal: destination path '.' already exists and is not an empty directory.\n ```\n - Stderr: (empty)\n\n## Context\n- failure_class: deterministic\n- failure_signature: setup|deterministic|script failed with exit code: ## stdout fatal: destination path '.' already exists and is not an empty directory.\n", + "internal.thread_id": "solve", + "current.preamble": "Goal: Added model class to app_list context\nDescription\n\t \n\t\t(last modified by Raffaele Salmaso)\n\t \nI need to manipulate the app_list in my custom admin view, and the easiest way to get the result is to have access to the model class (currently the dictionary is a serialized model).\nIn addition I would make the _build_app_dict method public, as it is used by the two views index and app_index.\n\n\n## Completed stages\n- **setup**: fail\n - Script: `git clone https://github.com/django/django.git . && git checkout 0456d3e42795481a186db05719300691fe2a1029 && python -m pip install -e .`\n - Stdout:\n ```\n fatal: destination path '.' already exists and is not an empty directory.\n ```\n - Stderr: (empty)\n- **solve**: success\n - Model: claude-opus-4-6, 8.1k tokens in / 2.7k out\n - Files: /tmp/django/django/contrib/admin/sites.py\n", "internal.retry_count.start": 1, "internal.retry_count.setup": 1, "internal.fidelity": "compact", "internal.node_visit_count": 1, - "command.output": "fatal: destination path '.' already exists and is not an empty directory.\n", - "outcome": "success" + "command.output": "", + "outcome": "success", + "thread.solve.current_node": "extract_patch" }, "logs": [], "node_outcomes": { + "start": { + "status": "success", + "duration_ms": 0 + }, + "setup": { + "status": "fail", + "context_updates": { + "command.stderr": "", + "command.output": "fatal: destination path '.' already exists and is not an empty directory.\n" + }, + "failure": { + "message": "Script failed with exit code: 128\n\n## stdout\nfatal: destination path '.' already exists and is not an empty directory.\n", + "failure_class": "deterministic" + }, + "duration_ms": 19 + }, + "extract_patch": { + "status": "success", + "context_updates": { + "command.stderr": "", + "command.output": "" + }, + "notes": "Script completed: git diff", + "duration_ms": 127 + }, "solve": { "status": "success", "context_updates": { @@ -57,31 +86,16 @@ "/tmp/django/django/contrib/admin/sites.py" ], "duration_ms": 111339 - }, - "start": { - "status": "success", - "duration_ms": 0 - }, - "setup": { - "status": "fail", - "context_updates": { - "command.stderr": "", - "command.output": "fatal: destination path '.' already exists and is not an empty directory.\n" - }, - "failure": { - "message": "Script failed with exit code: 128\n\n## stdout\nfatal: destination path '.' already exists and is not an empty directory.\n", - "failure_class": "deterministic" - }, - "duration_ms": 19 } }, - "next_node_id": "extract_patch", + "next_node_id": "exit", "loop_failure_signatures": { "setup|deterministic|script failed with exit code: ## stdout fatal: destination path '.' already exists and is not an empty directory.": 1 }, "node_visits": { "solve": 1, "start": 1, - "setup": 1 + "setup": 1, + "extract_patch": 1 } } \ No newline at end of file diff --git a/nodes/extract_patch/script_invocation.json b/nodes/extract_patch/script_invocation.json new file mode 100644 index 000000000..6d29afc80 --- /dev/null +++ b/nodes/extract_patch/script_invocation.json @@ -0,0 +1,5 @@ +{ + "command": "git diff", + "language": "shell", + "timeout_ms": null +} \ No newline at end of file diff --git a/nodes/extract_patch/script_timing.json b/nodes/extract_patch/script_timing.json new file mode 100644 index 000000000..1360ce73c --- /dev/null +++ b/nodes/extract_patch/script_timing.json @@ -0,0 +1,5 @@ +{ + "duration_ms": 127, + "exit_code": 0, + "timed_out": false +} \ No newline at end of file diff --git a/nodes/extract_patch/status.json b/nodes/extract_patch/status.json new file mode 100644 index 000000000..1caa200c4 --- /dev/null +++ b/nodes/extract_patch/status.json @@ -0,0 +1,6 @@ +{ + "status": "success", + "notes": "Script completed: git diff", + "failure_reason": null, + "timestamp": "2026-03-16T05:35:45.415477+00:00" +} \ No newline at end of file diff --git a/nodes/solve/diff.patch b/nodes/solve/diff.patch new file mode 100644 index 000000000..b7bb87f7f --- /dev/null +++ b/nodes/solve/diff.patch @@ -0,0 +1,577 @@ +diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py +new file mode 100644 +index 0000000..59f2148 +--- /dev/null ++++ b/django/contrib/admin/sites.py +@@ -0,0 +1,570 @@ ++import re ++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.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 _, 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 AlreadyRegistered(Exception): ++ pass ++ ++ ++class NotRegistered(Exception): ++ pass ++ ++ ++class AdminSite: ++ """ ++ 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 . ++ site_title = gettext_lazy('Django site admin') ++ ++ # Text to put in each page's <h1>. ++ site_header = gettext_lazy('Django administration') ++ ++ # Text to put at the top of the admin index page. ++ index_title = gettext_lazy('Site administration') ++ ++ # URL for the "View site" link at the top of each admin page. ++ 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 = {} # model_class class -> admin_class instance ++ self.name = name ++ self._actions = {'delete_selected': actions.delete_selected} ++ self._global_actions = self._actions.copy() ++ all_sites.add(self) ++ ++ def check(self, app_configs): ++ """ ++ Run the system checks on all ModelAdmins, except if they aren't ++ customized at all. ++ """ ++ if app_configs is None: ++ app_configs = apps.get_app_configs() ++ app_configs = set(app_configs) # Speed up lookups below ++ ++ 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): ++ """ ++ 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. ++ """ ++ 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 model in self._registry: ++ registered_admin = str(self._registry[model]) ++ msg = 'The model %s is already registered ' % model.__name__ ++ if registered_admin.endswith('.ModelAdmin'): ++ # Most likely registered without a ModelAdmin subclass. ++ msg += 'in app %r.' % re.sub(r'\.ModelAdmin$', '', registered_admin) ++ else: ++ msg += 'with %r.' % registered_admin ++ raise AlreadyRegistered(msg) ++ ++ # Ignore the registration if the model has been ++ # swapped out. ++ if not model._meta.swapped: ++ # If we got **options then dynamically construct a subclass of ++ # admin_class with those **options. ++ if 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. ++ options['__module__'] = __name__ ++ admin_class = type("%sAdmin" % model.__name__, (admin_class,), options) ++ ++ # Instantiate the admin class to save in the registry ++ self._registry[model] = admin_class(model, self) ++ ++ def unregister(self, model_or_iterable): ++ """ ++ Unregister the given model(s). ++ ++ If a model isn't already registered, raise NotRegistered. ++ """ ++ if isinstance(model_or_iterable, ModelBase): ++ model_or_iterable = [model_or_iterable] ++ for model in model_or_iterable: ++ if model not in self._registry: ++ raise NotRegistered('The model %s is not registered' % model.__name__) ++ del self._registry[model] ++ ++ def is_registered(self, model): ++ """ ++ Check if a model class is registered with this `AdminSite`. ++ """ ++ return model in self._registry ++ ++ def add_action(self, action, name=None): ++ """ ++ Register an action to be available globally. ++ """ ++ name = name or action.__name__ ++ self._actions[name] = action ++ self._global_actions[name] = action ++ ++ def disable_action(self, name): ++ """ ++ Disable a globally-registered action. Raise KeyError for invalid names. ++ """ ++ del self._actions[name] ++ ++ def get_action(self, name): ++ """ ++ Explicitly get a registered global action whether it's enabled or ++ not. Raise KeyError for invalid names. ++ """ ++ return self._global_actions[name] ++ ++ @property ++ def actions(self): ++ """ ++ Get all the enabled actions as an iterable of (name, func). ++ """ ++ return self._actions.items() ++ ++ def has_permission(self, request): ++ """ ++ Return True if the given HttpRequest has permission to view ++ *at least one* page in the admin site. ++ """ ++ return request.user.is_active and request.user.is_staff ++ ++ def admin_view(self, view, cacheable=False): ++ """ ++ 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. ++ """ ++ 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) ++ # Inner import to prevent django.contrib.admin (app) from ++ # importing django.contrib.auth.models.User (unrelated model). ++ 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) ++ # We add csrf_protect here so this function can be used as a utility ++ # function for any view, without having to repeat 'csrf_protect'. ++ if not getattr(view, 'csrf_exempt', False): ++ inner = csrf_protect(inner) ++ return update_wrapper(inner, view) ++ ++ def get_urls(self): ++ # 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. ++ 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) ++ ++ # Admin-site-wide views. ++ 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', ++ ), ++ ] ++ ++ # Add in each model's views, and create a list of valid URLS for the ++ # app_index ++ 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 there were ModelAdmins registered, we should have a list of app ++ # labels for which we need to allow access to the app_index view, ++ 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): ++ """ ++ 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. ++ """ ++ 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, ++ } ++ ++ def password_change(self, request, extra_context=None): ++ """ ++ Handle the "change password" task -- both form display and validation. ++ """ ++ 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): ++ """ ++ Display the "success" page after a password change. ++ """ ++ 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): ++ """ ++ Display the i18n JavaScript that the Django admin requires. ++ ++ `extra_context` is unused but present for consistency with the other ++ admin views. ++ """ ++ return JavaScriptCatalog.as_view(packages=['django.contrib.admin'])(request) ++ ++ def logout(self, request, extra_context=None): ++ """ ++ Log out the user for the given HttpRequest. ++ ++ This should *not* assume the user is already logged in. ++ """ ++ from django.contrib.auth.views import LogoutView ++ defaults = { ++ 'extra_context': { ++ **self.each_context(request), ++ # Since the user isn't logged out at this point, the value of ++ # has_permission must be overridden. ++ '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): ++ """ ++ Display the login form for the given HttpRequest. ++ """ ++ if request.method == 'GET' and self.has_permission(request): ++ # Already logged-in, redirect to admin index ++ index_path = reverse('admin:index', current_app=self.name) ++ return HttpResponseRedirect(index_path) ++ ++ # 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. ++ from django.contrib.admin.forms import AdminAuthenticationForm ++ from django.contrib.auth.views import LoginView ++ context = { ++ **self.each_context(request), ++ 'title': _('Log in'), ++ '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) ++ path = '%s/' % request.path_info ++ try: ++ match = resolve(path, urlconf) ++ except Resolver404: ++ pass ++ else: ++ if getattr(match.func, 'should_append_slash', True): ++ return HttpResponsePermanentRedirect(path) ++ raise Http404 ++ ++ def build_app_dict(self, request, label=None): ++ """ ++ Build the app dictionary. The optional `label` parameter filters models ++ of a specific app. ++ """ ++ 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) ++ ++ # Check whether user has any perm for this module. ++ # If so, add the module to the model_list. ++ 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], ++ } ++ ++ if label: ++ return app_dict.get(label) ++ return app_dict ++ ++ def get_app_list(self, request): ++ """ ++ Return a sorted list of all the installed apps that have been ++ registered in this site. ++ """ ++ app_dict = self.build_app_dict(request) ++ ++ # Sort the apps alphabetically. ++ app_list = sorted(app_dict.values(), key=lambda x: x['name'].lower()) ++ ++ # Sort the models alphabetically within each app. ++ for app in app_list: ++ app['models'].sort(key=lambda x: x['name']) ++ ++ return app_list ++ ++ def index(self, request, extra_context=None): ++ """ ++ Display the main admin index page, which lists all of the installed ++ apps that have been registered in this site. ++ """ ++ 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_dict = self.build_app_dict(request, app_label) ++ if not app_dict: ++ raise Http404('The requested admin page does not exist.') ++ # Sort the models alphabetically within each app. ++ app_dict['models'].sort(key=lambda x: x['name']) ++ context = { ++ **self.each_context(request), ++ 'title': _('%(app)s administration') % {'app': app_dict['name']}, ++ 'subtitle': None, ++ 'app_list': [app_dict], ++ '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) ++ ++ ++class DefaultAdminSite(LazyObject): ++ def _setup(self): ++ AdminSiteClass = import_string(apps.get_app_config('admin').default_site) ++ self._wrapped = AdminSiteClass() ++ ++ ++# 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. ++site = DefaultAdminSite() +\ No newline at end of file