瀏覽代碼

Fixed #24055 -- Keep reference to view class for resolve()

Collin Anderson 10 年之前
父節點
當前提交
a420f83e7d

+ 1 - 0
django/contrib/admin/options.py

@@ -583,6 +583,7 @@ class ModelAdmin(BaseModelAdmin):
         def wrap(view):
             def wrapper(*args, **kwargs):
                 return self.admin_site.admin_view(view)(*args, **kwargs)
+            wrapper.model_admin = self
             return update_wrapper(wrapper, view)
 
         info = self.model._meta.app_label, self.model._meta.model_name

+ 1 - 0
django/contrib/admin/sites.py

@@ -245,6 +245,7 @@ class AdminSite(object):
         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.

+ 2 - 0
django/views/generic/base.py

@@ -69,6 +69,8 @@ class View(object):
             self.args = args
             self.kwargs = kwargs
             return self.dispatch(request, *args, **kwargs)
+        view.view_class = cls
+        view.view_initkwargs = initkwargs
 
         # take name and docstring from class
         update_wrapper(view, cls, updated=())

+ 5 - 0
docs/ref/class-based-views/base.txt

@@ -65,6 +65,11 @@ View
 
             response = MyView.as_view()(request)
 
+        .. versionadded:: 1.9
+
+        The returned view has ``view_class`` and ``view_initkwargs``
+        attributes.
+
     .. method:: dispatch(request, *args, **kwargs)
 
         The ``view`` part of the view -- the method that accepts a ``request``

+ 5 - 0
docs/ref/contrib/admin/index.txt

@@ -1490,6 +1490,11 @@ templates used by the :class:`ModelAdmin` views:
 
         url(r'^my_view/$', self.admin_site.admin_view(self.my_view, cacheable=True))
 
+    .. versionadded:: 1.9
+
+    ``ModelAdmin`` views have ``model_admin`` attributes. Other
+    ``AdminSite`` views have ``admin_site`` attributes.
+
 .. method:: ModelAdmin.get_form(request, obj=None, **kwargs)
 
     Returns a :class:`~django.forms.ModelForm` class for use in the admin add

+ 7 - 1
docs/releases/1.9.txt

@@ -33,7 +33,7 @@ Minor features
 :mod:`django.contrib.admin`
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-* ...
+* Admin views now have ``model_admin`` or ``admin_site`` attributes.
 
 :mod:`django.contrib.auth`
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -108,6 +108,12 @@ Forms
 
 * ...
 
+Generic Views
+^^^^^^^^^^^^^
+
+* Class based views generated using ``as_view()`` now have ``view_class``
+  and ``view_initkwargs`` attributes.
+
 Internationalization
 ^^^^^^^^^^^^^^^^^^^^
 

+ 8 - 1
tests/admin_views/tests.py

@@ -11,7 +11,7 @@ from django.core.checks import Error
 from django.core.files import temp as tempfile
 from django.core.exceptions import ImproperlyConfigured
 from django.core.urlresolvers import (NoReverseMatch,
-    get_script_prefix, reverse, set_script_prefix)
+    get_script_prefix, resolve, reverse, set_script_prefix)
 # Register auth models with the admin.
 from django.contrib.auth import get_permission_codename
 from django.contrib.admin import ModelAdmin
@@ -56,6 +56,7 @@ from .models import (Article, BarAccount, CustomArticle, EmptyModel, FooAccount,
     Simple, UndeletableObject, UnchangeableObject, Choice, ShortMessage,
     Telegram, Pizza, Topping, FilteredManager, City, Restaurant, Worker,
     ParentWithDependentChildren, Character, FieldOverridePost, Color2)
+from . import customadmin
 from .admin import site, site2, CityAdmin
 
 
@@ -749,6 +750,12 @@ class AdminViewBasicTest(AdminViewBasicTestCase):
         with self.assertRaises(NoReverseMatch):
             reverse('admin:app_list', args=('admin_views2',))
 
+    def test_resolve_admin_views(self):
+        index_match = resolve('/test_admin/admin4/')
+        list_match = resolve('/test_admin/admin4/auth/user/')
+        self.assertIs(index_match.func.admin_site, customadmin.simple_site)
+        self.assertIsInstance(list_match.func.model_admin, customadmin.CustomPwdTemplateUserAdmin)
+
     def test_proxy_model_content_type_is_used_for_log_entries(self):
         """
         Log entries for proxy models should have the proxy model's content

+ 10 - 0
tests/generic_views/test_base.py

@@ -5,6 +5,7 @@ import unittest
 import warnings
 
 from django.core.exceptions import ImproperlyConfigured
+from django.core.urlresolvers import resolve
 from django.http import HttpResponse
 from django.utils import six
 from django.utils.deprecation import RemovedInDjango19Warning
@@ -329,6 +330,15 @@ class TemplateViewTest(TestCase):
         response = self.client.get('/template/content_type/')
         self.assertEqual(response['Content-Type'], 'text/plain')
 
+    def test_resolve_view(self):
+        match = resolve('/template/content_type/')
+        self.assertIs(match.func.view_class, TemplateView)
+        self.assertEqual(match.func.view_initkwargs['content_type'], 'text/plain')
+
+    def test_resolve_login_required_view(self):
+        match = resolve('/template/login_required/')
+        self.assertIs(match.func.view_class, TemplateView)
+
 
 @ignore_warnings(category=RemovedInDjango19Warning)
 @override_settings(ROOT_URLCONF='generic_views.urls')

+ 3 - 0
tests/generic_views/urls.py

@@ -3,6 +3,7 @@ from __future__ import unicode_literals
 
 from django.conf.urls import url
 from django.contrib.auth import views as auth_views
+from django.contrib.auth.decorators import login_required
 from django.views.decorators.cache import cache_page
 from django.views.generic import TemplateView
 
@@ -14,6 +15,8 @@ urlpatterns = [
     # TemplateView
     url(r'^template/no_template/$',
         TemplateView.as_view()),
+    url(r'^template/login_required/$',
+        login_required(TemplateView.as_view())),
     url(r'^template/simple/(?P<foo>\w+)/$',
         TemplateView.as_view(template_name='generic_views/about.html')),
     url(r'^template/custom/(?P<foo>\w+)/$',