diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
a
|
b
|
|
5 | 5 | from django.contrib.contenttypes.models import ContentType |
6 | 6 | from django.contrib.admin import widgets |
7 | 7 | from django.contrib.admin import helpers |
8 | | from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_ngettext, model_format_dict |
| 8 | from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_format_dict |
9 | 9 | from django.core.exceptions import PermissionDenied |
10 | 10 | from django.db import models, transaction |
11 | 11 | from django.db.models.fields import BLANK_CHOICE_DASH |
… |
… |
|
18 | 18 | from django.utils.functional import curry |
19 | 19 | from django.utils.text import capfirst, get_text_list |
20 | 20 | from django.utils.translation import ugettext as _ |
21 | | from django.utils.translation import ungettext, ugettext_lazy |
| 21 | from django.utils.translation import ungettext |
22 | 22 | from django.utils.encoding import force_unicode |
| 23 | from django.views.decorators.cache import never_cache |
23 | 24 | try: |
24 | 25 | set |
25 | 26 | except NameError: |
… |
… |
|
224 | 225 | def wrap(view): |
225 | 226 | def wrapper(*args, **kwargs): |
226 | 227 | return self.admin_site.admin_view(view)(*args, **kwargs) |
227 | | return update_wrapper(wrapper, view) |
| 228 | return never_cache(update_wrapper(wrapper, view)) |
228 | 229 | |
229 | 230 | info = self.admin_site.name, self.model._meta.app_label, self.model._meta.module_name |
230 | 231 | |
diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py
a
|
b
|
|
12 | 12 | from django.utils.translation import ugettext_lazy, ugettext as _ |
13 | 13 | from django.views.decorators.cache import never_cache |
14 | 14 | from django.conf import settings |
15 | | try: |
16 | | set |
17 | | except NameError: |
18 | | from sets import Set as set # Python 2.3 fallback |
19 | 15 | |
20 | 16 | ERROR_MESSAGE = ugettext_lazy("Please enter a correct username and password. Note that both fields are case-sensitive.") |
21 | 17 | LOGIN_FORM_KEY = 'this_is_the_login_form' |
… |
… |
|
114 | 110 | name = name or action.__name__ |
115 | 111 | self._actions[name] = action |
116 | 112 | self._global_actions[name] = action |
117 | | |
| 113 | |
118 | 114 | def disable_action(self, name): |
119 | 115 | """ |
120 | 116 | Disable a globally-registered action. Raises KeyError for invalid names. |
121 | 117 | """ |
122 | 118 | del self._actions[name] |
123 | | |
| 119 | |
124 | 120 | def get_action(self, name): |
125 | 121 | """ |
126 | 122 | Explicitally get a registered global action wheather it's enabled or |
127 | 123 | not. Raises KeyError for invalid names. |
128 | 124 | """ |
129 | 125 | return self._global_actions[name] |
130 | | |
| 126 | |
131 | 127 | def actions(self): |
132 | 128 | """ |
133 | 129 | Get all the enabled actions as an iterable of (name, func). |
… |
… |
|
182 | 178 | if not self.has_permission(request): |
183 | 179 | return self.login(request) |
184 | 180 | return view(request, *args, **kwargs) |
185 | | return update_wrapper(inner, view) |
| 181 | return never_cache(update_wrapper(inner, view)) |
186 | 182 | |
187 | 183 | def get_urls(self): |
188 | 184 | from django.conf.urls.defaults import patterns, url, include |
diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt
a
|
b
|
|
757 | 757 | |
758 | 758 | .. note:: |
759 | 759 | |
760 | | Notice that the custom patterns are included *before* the regular admin |
| 760 | Note how we included our custom patterns *before* the regular admin |
761 | 761 | URLs: the admin URL patterns are very permissive and will match nearly |
762 | 762 | anything, so you'll usually want to prepend your custom URLs to the built-in |
763 | 763 | ones. |
764 | 764 | |
765 | | Note, however, that the ``self.my_view`` function registered above will *not* |
766 | | have any permission check done; it'll be accessible to the general public. Since |
767 | | this is usually not what you want, Django provides a convience wrapper to check |
768 | | permissions. This wrapper is :meth:`AdminSite.admin_view` (i.e. |
769 | | ``self.admin_site.admin_view`` inside a ``ModelAdmin`` instance); use it like |
770 | | so:: |
| 765 | However, the ``self.my_view`` function registered above suffers from two |
| 766 | problems: |
| 767 | |
| 768 | * It will *not* have any permission check done; it'll be accessible to the |
| 769 | general public. |
| 770 | * It is not being marked as a non-cacheable and so, if it gets data from the |
| 771 | database, it could show outdated information because of content caching being |
| 772 | applied when the caching middleware is active. |
| 773 | |
| 774 | Since this is usually not what you want, Django provides a convenience wrapper |
| 775 | to check permissions and mark the view as non-cacheable. This wrapper is |
| 776 | :meth:`AdminSite.admin_view` (i.e. ``self.admin_site.admin_view`` inside a |
| 777 | ``ModelAdmin`` instance); use it like so:: |
771 | 778 | |
772 | 779 | class MyModelAdmin(admin.ModelAdmin): |
773 | 780 | def get_urls(self): |
… |
… |
|
781 | 788 | |
782 | 789 | (r'^my_view/$', self.admin_site.admin_view(self.my_view)) |
783 | 790 | |
784 | | This wrapping will protect ``self.my_view`` from unauthorized access. |
| 791 | This wrapping will protect ``self.my_view`` from unauthorized access and will |
| 792 | apply the ``django.views.decorators.cache.never_cache`` decorator to make sure |
| 793 | it is not cached if the cache middleware is active. |
785 | 794 | |
786 | 795 | .. method:: ModelAdmin.formfield_for_foreignkey(self, db_field, request, **kwargs) |
787 | 796 | |
… |
… |
|
1331 | 1340 | .. versionadded:: 1.1 |
1332 | 1341 | |
1333 | 1342 | Just like :class:`ModelAdmin`, :class:`AdminSite` provides a |
1334 | | :meth:`~django.contrib.admin.ModelAdmin.get_urls()` method |
1335 | | that can be overridden to define additional views for the site. To add |
1336 | | a new view to your admin site, extend the base |
1337 | | :meth:`~django.contrib.admin.ModelAdmin.get_urls()` method to include |
1338 | | a pattern for your new view. |
| 1343 | :meth:`~django.contrib.admin.ModelAdmin.get_urls()` method that can be |
| 1344 | overridden to define additional views for the site. To add a new view to your |
| 1345 | admin site, extend the base ``get_urls()`` method to include a pattern for your |
| 1346 | new view. And, just like it is also described in the |
| 1347 | :meth:`~django.contrib.admin.ModelAdmin.get_urls()` section, you will want to |
| 1348 | wrap your custom view with the |
| 1349 | :meth:`~django.contrib.admin.AdminSite.admin_view()` :class:`AdminSite` method |
| 1350 | to get access control and never-caching features for it. |
1339 | 1351 | |
1340 | 1352 | .. note:: |
1341 | 1353 | Any view you render that uses the admin templates, or extends the base |
diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py
a
|
b
|
|
54 | 54 | response = self.client.get('/test_admin/%s/admin_views/section/add/' % self.urlbit) |
55 | 55 | self.failUnlessEqual(response.status_code, 200) |
56 | 56 | |
| 57 | def testMaxAgeModelAdminView(self): |
| 58 | """ |
| 59 | Because cache time can be set by middleware, ensure max-age is explicity 0 |
| 60 | (non-model-specific view) |
| 61 | """ |
| 62 | response = self.client.get('/test_admin/%s/admin_views/' % self.urlbit) |
| 63 | |
| 64 | from django.utils.cache import get_max_age |
| 65 | self.failUnlessEqual(get_max_age(response), 0) |
| 66 | |
| 67 | def testMaxAgeModelView(self): |
| 68 | """ |
| 69 | Because cache time can be set by middleware, ensure max-age is explicity 0 |
| 70 | (model-specific view) |
| 71 | """ |
| 72 | response = self.client.get('/test_admin/%s/admin_views/section/add/' % self.urlbit) |
| 73 | |
| 74 | from django.utils.cache import get_max_age |
| 75 | self.failUnlessEqual(get_max_age(response), 0) |
| 76 | |
57 | 77 | def testAddWithGETArgs(self): |
58 | 78 | response = self.client.get('/test_admin/%s/admin_views/section/add/' % self.urlbit, {'name': 'My Section'}) |
59 | 79 | self.failUnlessEqual(response.status_code, 200) |