Prechádzať zdrojové kódy

Fixed #29296 -- Fixed crashes in admindocs when a view is a callable object.

Paul Donohue 7 rokov pred
rodič
commit
33a0b7ac81

+ 1 - 0
AUTHORS

@@ -637,6 +637,7 @@ answer newbie questions, and generally made Django that much better:
     Paul Bissex <http://e-scribe.com/>
     Paul Collier <paul@paul-collier.com>
     Paul Collins <paul.collins.iii@gmail.com>
+    Paul Donohue <django@PaulSD.com>
     Paul Lanier <planier@google.com>
     Paul McLanahan <paul@mclanahan.net>
     Paul McMillan <Paul@McMillan.ws>

+ 3 - 1
django/contrib/admindocs/middleware.py

@@ -2,6 +2,8 @@ from django.conf import settings
 from django.http import HttpResponse
 from django.utils.deprecation import MiddlewareMixin
 
+from .utils import get_view_name
+
 
 class XViewMiddleware(MiddlewareMixin):
     """
@@ -24,5 +26,5 @@ class XViewMiddleware(MiddlewareMixin):
         if request.method == 'HEAD' and (request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS or
                                          (request.user.is_active and request.user.is_staff)):
             response = HttpResponse()
-            response['X-View'] = "%s.%s" % (view_func.__module__, view_func.__name__)
+            response['X-View'] = get_view_name(view_func)
             return response

+ 6 - 0
django/contrib/admindocs/utils.py

@@ -18,6 +18,12 @@ else:
     docutils_is_available = True
 
 
+def get_view_name(view_func):
+    mod_name = view_func.__module__
+    view_name = getattr(view_func, '__qualname__', view_func.__class__.__name__)
+    return mod_name + '.' + view_name
+
+
 def trim_docstring(docstring):
     """
     Uniformly trim leading/trailing whitespace from docstrings.

+ 3 - 6
django/contrib/admindocs/views.py

@@ -23,6 +23,8 @@ from django.utils.inspect import (
 from django.utils.translation import gettext as _
 from django.views.generic import TemplateView
 
+from .utils import get_view_name
+
 # Exclude methods starting with these strings from documentation
 MODEL_METHODS_EXCLUDE = ('_', 'add_', 'delete', 'save', 'set_')
 
@@ -124,18 +126,13 @@ class TemplateFilterIndexView(BaseAdminDocsView):
 class ViewIndexView(BaseAdminDocsView):
     template_name = 'admin_doc/view_index.html'
 
-    @staticmethod
-    def _get_full_name(func):
-        mod_name = func.__module__
-        return '%s.%s' % (mod_name, func.__qualname__)
-
     def get_context_data(self, **kwargs):
         views = []
         urlconf = import_module(settings.ROOT_URLCONF)
         view_functions = extract_views_from_urlpatterns(urlconf.urlpatterns)
         for (func, regex, namespace, name) in view_functions:
             views.append({
-                'full_name': self._get_full_name(func),
+                'full_name': get_view_name(func),
                 'url': simplify_regex(regex),
                 'url_name': ':'.join((namespace or []) + (name and [name] or [])),
                 'namespace': ':'.join((namespace or [])),

+ 3 - 0
docs/releases/1.11.13.txt

@@ -12,3 +12,6 @@ Bugfixes
 * Fixed a regression in Django 1.11.8 where altering a field with a unique
   constraint may drop and rebuild more foreign keys than necessary
   (:ticket:`29193`).
+
+* Fixed crashes in ``django.contrib.admindocs`` when a view is a callable
+  object, such as ``django.contrib.syndication.views.Feed`` (:ticket:`29296`).

+ 3 - 0
docs/releases/2.0.5.txt

@@ -15,3 +15,6 @@ Bugfixes
 * Fixed a regression in Django 1.11.8 where altering a field with a unique
   constraint may drop and rebuild more foreign keys than necessary
   (:ticket:`29193`).
+
+* Fixed crashes in ``django.contrib.admindocs`` when a view is a callable
+  object, such as ``django.contrib.syndication.views.Feed`` (:ticket:`29296`).

+ 5 - 0
tests/admin_docs/test_middleware.py

@@ -40,3 +40,8 @@ class XViewMiddlewareTest(TestDataMixin, AdminDocsTestCase):
         user.save()
         response = self.client.head('/xview/class/')
         self.assertNotIn('X-View', response)
+
+    def test_callable_object_view(self):
+        self.client.force_login(self.superuser)
+        response = self.client.head('/xview/callable_object/')
+        self.assertEqual(response['X-View'], 'admin_docs.views.XViewCallableObject')

+ 6 - 0
tests/admin_docs/test_views.py

@@ -51,6 +51,12 @@ class AdminDocViewTests(TestDataMixin, AdminDocsTestCase):
         )
         self.assertContains(response, 'Views by namespace test')
         self.assertContains(response, 'Name: <code>test:func</code>.')
+        self.assertContains(
+            response,
+            '<h3><a href="/admindocs/views/admin_docs.views.XViewCallableObject/">'
+            '/xview/callable_object_without_xview/</a></h3>',
+            html=True,
+        )
 
     def test_view_index_with_method(self):
         """

+ 2 - 0
tests/admin_docs/urls.py

@@ -13,4 +13,6 @@ urlpatterns = [
     url(r'^', include(ns_patterns, namespace='test')),
     url(r'^xview/func/$', views.xview_dec(views.xview)),
     url(r'^xview/class/$', views.xview_dec(views.XViewClass.as_view())),
+    url(r'^xview/callable_object/$', views.xview_dec(views.XViewCallableObject())),
+    url(r'^xview/callable_object_without_xview/$', views.XViewCallableObject()),
 ]

+ 5 - 0
tests/admin_docs/views.py

@@ -13,3 +13,8 @@ def xview(request):
 class XViewClass(View):
     def get(self, request):
         return HttpResponse()
+
+
+class XViewCallableObject(View):
+    def __call__(self, request):
+        return HttpResponse()