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) |