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

This commit is contained in:
Collin Anderson 2014-12-27 02:16:53 -05:00 committed by Loic Bistuer
parent 67235fd4ef
commit a420f83e7d
9 changed files with 42 additions and 2 deletions

View File

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

View File

@ -245,6 +245,7 @@ class AdminSite(object):
def wrap(view, cacheable=False): def wrap(view, cacheable=False):
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
return self.admin_view(view, cacheable)(*args, **kwargs) return self.admin_view(view, cacheable)(*args, **kwargs)
wrapper.admin_site = self
return update_wrapper(wrapper, view) return update_wrapper(wrapper, view)
# Admin-site-wide views. # Admin-site-wide views.

View File

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

View File

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

View File

@ -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)) 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) .. method:: ModelAdmin.get_form(request, obj=None, **kwargs)
Returns a :class:`~django.forms.ModelForm` class for use in the admin add Returns a :class:`~django.forms.ModelForm` class for use in the admin add

View File

@ -33,7 +33,7 @@ Minor features
:mod:`django.contrib.admin` :mod:`django.contrib.admin`
^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
* ... * Admin views now have ``model_admin`` or ``admin_site`` attributes.
:mod:`django.contrib.auth` :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 Internationalization
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^

View File

@ -11,7 +11,7 @@ from django.core.checks import Error
from django.core.files import temp as tempfile from django.core.files import temp as tempfile
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import (NoReverseMatch, 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. # Register auth models with the admin.
from django.contrib.auth import get_permission_codename from django.contrib.auth import get_permission_codename
from django.contrib.admin import ModelAdmin from django.contrib.admin import ModelAdmin
@ -56,6 +56,7 @@ from .models import (Article, BarAccount, CustomArticle, EmptyModel, FooAccount,
Simple, UndeletableObject, UnchangeableObject, Choice, ShortMessage, Simple, UndeletableObject, UnchangeableObject, Choice, ShortMessage,
Telegram, Pizza, Topping, FilteredManager, City, Restaurant, Worker, Telegram, Pizza, Topping, FilteredManager, City, Restaurant, Worker,
ParentWithDependentChildren, Character, FieldOverridePost, Color2) ParentWithDependentChildren, Character, FieldOverridePost, Color2)
from . import customadmin
from .admin import site, site2, CityAdmin from .admin import site, site2, CityAdmin
@ -749,6 +750,12 @@ class AdminViewBasicTest(AdminViewBasicTestCase):
with self.assertRaises(NoReverseMatch): with self.assertRaises(NoReverseMatch):
reverse('admin:app_list', args=('admin_views2',)) 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): def test_proxy_model_content_type_is_used_for_log_entries(self):
""" """
Log entries for proxy models should have the proxy model's content Log entries for proxy models should have the proxy model's content

View File

@ -5,6 +5,7 @@ import unittest
import warnings import warnings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import resolve
from django.http import HttpResponse from django.http import HttpResponse
from django.utils import six from django.utils import six
from django.utils.deprecation import RemovedInDjango19Warning from django.utils.deprecation import RemovedInDjango19Warning
@ -329,6 +330,15 @@ class TemplateViewTest(TestCase):
response = self.client.get('/template/content_type/') response = self.client.get('/template/content_type/')
self.assertEqual(response['Content-Type'], 'text/plain') 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) @ignore_warnings(category=RemovedInDjango19Warning)
@override_settings(ROOT_URLCONF='generic_views.urls') @override_settings(ROOT_URLCONF='generic_views.urls')

View File

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