Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Added AdminSite attributes for easily changing admin title.

AdminSite now has overridable site_header, site_title and index_title attributes. Changed
each admin view to pass these to the context (in a new AdminSite.each_context() method).
The intent here is to make it easier to override these things in the common case, instead of
having to override a template, which is a bigger burden.
  • Loading branch information...
commit a962286b74f1e8c8cb19fb45a057800da8c2fb56 1 parent 273a1e6
Adrian Holovaty authored September 06, 2013
118  django/contrib/admin/options.py
@@ -1183,16 +1183,16 @@ def add_view(self, request, form_url='', extra_context=None):
1183 1183
             inline_admin_formsets.append(inline_admin_formset)
1184 1184
             media = media + inline_admin_formset.media
1185 1185
 
1186  
-        context = {
1187  
-            'title': _('Add %s') % force_text(opts.verbose_name),
1188  
-            'adminform': adminForm,
1189  
-            'is_popup': IS_POPUP_VAR in request.REQUEST,
1190  
-            'media': media,
1191  
-            'inline_admin_formsets': inline_admin_formsets,
1192  
-            'errors': helpers.AdminErrorList(form, formsets),
1193  
-            'app_label': opts.app_label,
1194  
-            'preserved_filters': self.get_preserved_filters(request),
1195  
-        }
  1186
+        context = dict(self.admin_site.each_context(),
  1187
+            title=_('Add %s') % force_text(opts.verbose_name),
  1188
+            adminform=adminForm,
  1189
+            is_popup=IS_POPUP_VAR in request.REQUEST,
  1190
+            media=media,
  1191
+            inline_admin_formsets=inline_admin_formsets,
  1192
+            errors=helpers.AdminErrorList(form, formsets),
  1193
+            app_label=opts.app_label,
  1194
+            preserved_filters=self.get_preserved_filters(request),
  1195
+        )
1196 1196
         context.update(extra_context or {})
1197 1197
         return self.render_change_form(request, context, form_url=form_url, add=True)
1198 1198
 
@@ -1254,18 +1254,18 @@ def change_view(self, request, object_id, form_url='', extra_context=None):
1254 1254
             inline_admin_formsets.append(inline_admin_formset)
1255 1255
             media = media + inline_admin_formset.media
1256 1256
 
1257  
-        context = {
1258  
-            'title': _('Change %s') % force_text(opts.verbose_name),
1259  
-            'adminform': adminForm,
1260  
-            'object_id': object_id,
1261  
-            'original': obj,
1262  
-            'is_popup': IS_POPUP_VAR in request.REQUEST,
1263  
-            'media': media,
1264  
-            'inline_admin_formsets': inline_admin_formsets,
1265  
-            'errors': helpers.AdminErrorList(form, formsets),
1266  
-            'app_label': opts.app_label,
1267  
-            'preserved_filters': self.get_preserved_filters(request),
1268  
-        }
  1257
+        context = dict(self.admin_site.each_context(),
  1258
+            title=_('Change %s') % force_text(opts.verbose_name),
  1259
+            adminform=adminForm,
  1260
+            object_id=object_id,
  1261
+            original=obj,
  1262
+            is_popup=IS_POPUP_VAR in request.REQUEST,
  1263
+            media=media,
  1264
+            inline_admin_formsets=inline_admin_formsets,
  1265
+            errors=helpers.AdminErrorList(form, formsets),
  1266
+            app_label=opts.app_label,
  1267
+            preserved_filters=self.get_preserved_filters(request),
  1268
+        )
1269 1269
         context.update(extra_context or {})
1270 1270
         return self.render_change_form(request, context, change=True, obj=obj, form_url=form_url)
1271 1271
 
@@ -1400,23 +1400,23 @@ def changelist_view(self, request, extra_context=None):
1400 1400
         selection_note_all = ungettext('%(total_count)s selected',
1401 1401
             'All %(total_count)s selected', cl.result_count)
1402 1402
 
1403  
-        context = {
1404  
-            'module_name': force_text(opts.verbose_name_plural),
1405  
-            'selection_note': _('0 of %(cnt)s selected') % {'cnt': len(cl.result_list)},
1406  
-            'selection_note_all': selection_note_all % {'total_count': cl.result_count},
1407  
-            'title': cl.title,
1408  
-            'is_popup': cl.is_popup,
1409  
-            'cl': cl,
1410  
-            'media': media,
1411  
-            'has_add_permission': self.has_add_permission(request),
1412  
-            'opts': cl.opts,
1413  
-            'app_label': app_label,
1414  
-            'action_form': action_form,
1415  
-            'actions_on_top': self.actions_on_top,
1416  
-            'actions_on_bottom': self.actions_on_bottom,
1417  
-            'actions_selection_counter': self.actions_selection_counter,
1418  
-            'preserved_filters': self.get_preserved_filters(request),
1419  
-        }
  1403
+        context = dict(self.admin_site.each_context(),
  1404
+            module_name=force_text(opts.verbose_name_plural),
  1405
+            selection_note=_('0 of %(cnt)s selected') % {'cnt': len(cl.result_list)},
  1406
+            selection_note_all=selection_note_all % {'total_count': cl.result_count},
  1407
+            title=cl.title,
  1408
+            is_popup=cl.is_popup,
  1409
+            cl=cl,
  1410
+            media=media,
  1411
+            has_add_permission=self.has_add_permission(request),
  1412
+            opts=cl.opts,
  1413
+            app_label=app_label,
  1414
+            action_form=action_form,
  1415
+            actions_on_top=self.actions_on_top,
  1416
+            actions_on_bottom=self.actions_on_bottom,
  1417
+            actions_selection_counter=self.actions_selection_counter,
  1418
+            preserved_filters=self.get_preserved_filters(request),
  1419
+        )
1420 1420
         context.update(extra_context or {})
1421 1421
 
1422 1422
         return TemplateResponse(request, self.change_list_template or [
@@ -1483,17 +1483,17 @@ def delete_view(self, request, object_id, extra_context=None):
1483 1483
         else:
1484 1484
             title = _("Are you sure?")
1485 1485
 
1486  
-        context = {
1487  
-            "title": title,
1488  
-            "object_name": object_name,
1489  
-            "object": obj,
1490  
-            "deleted_objects": deleted_objects,
1491  
-            "perms_lacking": perms_needed,
1492  
-            "protected": protected,
1493  
-            "opts": opts,
1494  
-            "app_label": app_label,
1495  
-            'preserved_filters': self.get_preserved_filters(request),
1496  
-        }
  1486
+        context = dict(self.admin_site.each_context(),
  1487
+            title=title,
  1488
+            object_name=object_name,
  1489
+            object=obj,
  1490
+            deleted_objects=deleted_objects,
  1491
+            perms_lacking=perms_needed,
  1492
+            protected=protected,
  1493
+            opts=opts,
  1494
+            app_label=app_label,
  1495
+            preserved_filters=self.get_preserved_filters(request),
  1496
+        )
1497 1497
         context.update(extra_context or {})
1498 1498
 
1499 1499
         return TemplateResponse(request, self.delete_confirmation_template or [
@@ -1520,15 +1520,15 @@ def history_view(self, request, object_id, extra_context=None):
1520 1520
             content_type__id__exact=ContentType.objects.get_for_model(model).id
1521 1521
         ).select_related().order_by('action_time')
1522 1522
 
1523  
-        context = {
1524  
-            'title': _('Change history: %s') % force_text(obj),
1525  
-            'action_list': action_list,
1526  
-            'module_name': capfirst(force_text(opts.verbose_name_plural)),
1527  
-            'object': obj,
1528  
-            'app_label': app_label,
1529  
-            'opts': opts,
1530  
-            'preserved_filters': self.get_preserved_filters(request),
1531  
-        }
  1523
+        context = dict(self.admin_site.each_context(),
  1524
+            title=_('Change history: %s') % force_text(obj),
  1525
+            action_list=action_list,
  1526
+            module_name=capfirst(force_text(opts.verbose_name_plural)),
  1527
+            object=obj,
  1528
+            app_label=app_label,
  1529
+            opts=opts,
  1530
+            preserved_filters=self.get_preserved_filters(request),
  1531
+        )
1532 1532
         context.update(extra_context or {})
1533 1533
         return TemplateResponse(request, self.object_history_template or [
1534 1534
             "admin/%s/%s/object_history.html" % (app_label, opts.model_name),
55  django/contrib/admin/sites.py
@@ -34,6 +34,16 @@ class AdminSite(object):
34 34
     functions that present a full admin interface for the collection of registered
35 35
     models.
36 36
     """
  37
+
  38
+    # Text to put at the end of each page's <title>.
  39
+    site_title = _('Django site admin')
  40
+
  41
+    # Text to put in each page's <h1>.
  42
+    site_header = _('Django administration')
  43
+
  44
+    # Text to put at the top of the admin index page.
  45
+    index_title = _('Site administration')
  46
+
37 47
     login_form = None
38 48
     index_template = None
39 49
     app_index_template = None
@@ -236,6 +246,16 @@ def wrapper(*args, **kwargs):
236 246
     def urls(self):
237 247
         return self.get_urls(), self.app_name, self.name
238 248
 
  249
+    def each_context(self):
  250
+        """
  251
+        Returns a dictionary of variables to put in the template context for
  252
+        *every* page in the admin site.
  253
+        """
  254
+        return {
  255
+            'site_title': self.site_title,
  256
+            'site_header': self.site_header,
  257
+        }
  258
+
239 259
     def password_change(self, request):
240 260
         """
241 261
         Handles the "change password" task -- both form display and validation.
@@ -244,7 +264,8 @@ def password_change(self, request):
244 264
         url = reverse('admin:password_change_done', current_app=self.name)
245 265
         defaults = {
246 266
             'current_app': self.name,
247  
-            'post_change_redirect': url
  267
+            'post_change_redirect': url,
  268
+            'extra_context': self.each_context(),
248 269
         }
249 270
         if self.password_change_template is not None:
250 271
             defaults['template_name'] = self.password_change_template
@@ -257,7 +278,7 @@ def password_change_done(self, request, extra_context=None):
257 278
         from django.contrib.auth.views import password_change_done
258 279
         defaults = {
259 280
             'current_app': self.name,
260  
-            'extra_context': extra_context or {},
  281
+            'extra_context': dict(self.each_context(), **(extra_context or {})),
261 282
         }
262 283
         if self.password_change_done_template is not None:
263 284
             defaults['template_name'] = self.password_change_done_template
@@ -286,7 +307,7 @@ def logout(self, request, extra_context=None):
286 307
         from django.contrib.auth.views import logout
287 308
         defaults = {
288 309
             'current_app': self.name,
289  
-            'extra_context': extra_context or {},
  310
+            'extra_context': dict(self.each_context(), **(extra_context or {})),
290 311
         }
291 312
         if self.logout_template is not None:
292 313
             defaults['template_name'] = self.logout_template
@@ -298,11 +319,11 @@ def login(self, request, extra_context=None):
298 319
         Displays the login form for the given HttpRequest.
299 320
         """
300 321
         from django.contrib.auth.views import login
301  
-        context = {
302  
-            'title': _('Log in'),
303  
-            'app_path': request.get_full_path(),
304  
-            REDIRECT_FIELD_NAME: request.get_full_path(),
305  
-        }
  322
+        context = dict(self.each_context(),
  323
+            title=_('Log in'),
  324
+            app_path=request.get_full_path(),
  325
+        )
  326
+        context[REDIRECT_FIELD_NAME] = request.get_full_path()
306 327
         context.update(extra_context or {})
307 328
 
308 329
         defaults = {
@@ -366,10 +387,10 @@ def index(self, request, extra_context=None):
366 387
         for app in app_list:
367 388
             app['models'].sort(key=lambda x: x['name'])
368 389
 
369  
-        context = {
370  
-            'title': _('Site administration'),
371  
-            'app_list': app_list,
372  
-        }
  390
+        context = dict(self.each_context(),
  391
+            title=self.index_title,
  392
+            app_list=app_list,
  393
+        )
373 394
         context.update(extra_context or {})
374 395
         return TemplateResponse(request, self.index_template or
375 396
                                 'admin/index.html', context,
@@ -420,11 +441,11 @@ def app_index(self, request, app_label, extra_context=None):
420 441
             raise Http404('The requested admin page does not exist.')
421 442
         # Sort the models alphabetically within each app.
422 443
         app_dict['models'].sort(key=lambda x: x['name'])
423  
-        context = {
424  
-            'title': _('%s administration') % capfirst(app_label),
425  
-            'app_list': [app_dict],
426  
-            'app_label': app_label,
427  
-        }
  444
+        context = dict(self.each_context(),
  445
+            title=_('%s administration') % capfirst(app_label),
  446
+            app_list=[app_dict],
  447
+            app_label=app_label,
  448
+        )
428 449
         context.update(extra_context or {})
429 450
 
430 451
         return TemplateResponse(request, self.app_index_template or [
5  django/contrib/admin/templates/admin/base_site.html
... ...
@@ -1,10 +1,9 @@
1 1
 {% extends "admin/base.html" %}
2  
-{% load i18n %}
3 2
 
4  
-{% block title %}{{ title }} | {% trans 'Django site admin' %}{% endblock %}
  3
+{% block title %}{{ title }} | {{ site_title }}{% endblock %}
5 4
 
6 5
 {% block branding %}
7  
-<h1 id="site-name">{% trans 'Django administration' %}</h1>
  6
+<h1 id="site-name">{{ site_header }}</h1>
8 7
 {% endblock %}
9 8
 
10 9
 {% block nav-global %}{% endblock %}
17  docs/ref/contrib/admin/index.txt
@@ -2162,7 +2162,7 @@ creating your own ``AdminSite`` instance (see below), and changing the
2162 2162
     Python class), and register your models and ``ModelAdmin`` subclasses
2163 2163
     with it instead of using the default.
2164 2164
 
2165  
-    When constructing an instance of an ``AdminSite``, you are able to provide
  2165
+    When constructing an instance of an ``AdminSite``, you can provide
2166 2166
     a unique instance name using the ``name`` argument to the constructor. This
2167 2167
     instance name is used to identify the instance, especially when
2168 2168
     :ref:`reversing admin URLs <admin-reverse-urls>`. If no instance name is
@@ -2174,6 +2174,21 @@ creating your own ``AdminSite`` instance (see below), and changing the
2174 2174
 Templates can override or extend base admin templates as described in
2175 2175
 `Overriding Admin Templates`_.
2176 2176
 
  2177
+.. versionadded:: 1.6
  2178
+.. attribute:: AdminSite.site_header
  2179
+    The text to put at the top of each admin page, as an ``<h1>`` (a string).
  2180
+    By default, this is "Django administration".
  2181
+
  2182
+.. versionadded:: 1.6
  2183
+.. attribute:: AdminSite.site_title
  2184
+    The text to put at the end of each admin page's ``<title>`` (a string). By
  2185
+    default, this is "Django site admin".
  2186
+
  2187
+.. versionadded:: 1.6
  2188
+.. attribute:: AdminSite.index_title
  2189
+    The text to put at the top of the admin index page (a string). By default,
  2190
+    this is "Site administration".
  2191
+
2177 2192
 .. attribute:: AdminSite.index_template
2178 2193
 
2179 2194
     Path to a custom template that will be used by the admin site main index

2 notes on commit a962286

Tim Graham

Couple of issues here:
1. we're on 1.7 now
2. these docs are formatted weirdly when you build them. I think . . versionadded:: 1.6 should probably go below each ..attribute::
3. We try to add new features to the release notes as well

I can fix all this if you want.

p.s. After doing things "the hard way" in the tutorial, we could point out this feature there as well.

Adrian Holovaty

Thanks for the thoughts!

  1. I'm backporting it to 1.6.
  2. Will fix that -- thanks.
  3. Yup, I know. Working on it. Not sure yet whether I'll change the tutorial bit, because that section appears to double as an introduction to template overriding, so it may not be worth removing.
Daniele Procida
Owner

This introduces a problem with some auth templates - https://code.djangoproject.com/ticket/21293.

Aymeric Augustin
Owner

This problem is blocking the 1.7 release.

@adrianholovaty Would you have a few minutes to take a look at the ticket?

Please sign in to comment.
Something went wrong with that request. Please try again.