From 29c1151a5577cccfdb18109ccb42330cde124d89 Mon Sep 17 00:00:00 2001
From: Julia Matsieva <julia.matsieva@gmail.com>
Date: Mon, 16 Jun 2014 00:39:18 -0700
Subject: [PATCH] Fixed #22756 -- Added view name to technical 404 template if
 Http404 is raised.

Thanks Keryn Knight for the suggestion.
---
 AUTHORS                              |  1 +
 django/views/debug.py                | 27 ++++++++++++++++++++++++++-
 tests/view_tests/tests/test_debug.py | 11 +++++++++++
 tests/view_tests/urls.py             |  3 +++
 tests/view_tests/views.py            | 13 ++++++++++++-
 5 files changed, 53 insertions(+), 2 deletions(-)

diff --git a/AUTHORS b/AUTHORS
index 13b6bdee5e..2fb754b543 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -430,6 +430,7 @@ answer newbie questions, and generally made Django that much better:
     Orestis Markou <orestis@orestis.gr>
     Andrés Torres Marroquín <andres.torres.marroquin@gmail.com>
     Pablo Martín <goinnn@gmail.com>
+    Julia Matsieva <julia.matsieva@gmail.com>
     Takashi Matsuo <matsuo.takashi@gmail.com>
     Zlatko Mašek <zlatko.masek@gmail.com>
     Yasushi Masuda <whosaysni@gmail.com>
diff --git a/django/views/debug.py b/django/views/debug.py
index 2997f2da7c..ac81ecaf9e 100644
--- a/django/views/debug.py
+++ b/django/views/debug.py
@@ -7,6 +7,7 @@ import sys
 import types
 
 from django.conf import settings
+from django.core.urlresolvers import resolve, Resolver404
 from django.http import (HttpResponse, HttpResponseNotFound, HttpRequest,
     build_request_repr)
 from django.template import Template, Context, TemplateDoesNotExist
@@ -496,6 +497,23 @@ def technical_404_response(request, exception):
     if isinstance(urlconf, types.ModuleType):
         urlconf = urlconf.__name__
 
+    caller = ''
+    try:
+        resolver_match = resolve(request.path)
+    except Resolver404:
+        pass
+    else:
+        obj = resolver_match.func
+
+        if hasattr(obj, '__name__'):
+            caller = obj.__name__
+        elif hasattr(obj, '__class__') and hasattr(obj.__class__, '__name__'):
+            caller = obj.__class__.__name__
+
+        if hasattr(obj, '__module__'):
+            module = obj.__module__
+            caller = '%s.%s' % (module, caller)
+
     t = Template(TECHNICAL_404_TEMPLATE, name='Technical 404 template')
     c = Context({
         'urlconf': urlconf,
@@ -505,6 +523,7 @@ def technical_404_response(request, exception):
         'reason': force_bytes(exception, errors='replace'),
         'request': request,
         'settings': get_safe_settings(),
+        'raising_view_name': caller,
     })
     return HttpResponseNotFound(t.render(c), content_type='text/html')
 
@@ -1091,8 +1110,14 @@ TECHNICAL_404_TEMPLATE = """
       </tr>
       <tr>
         <th>Request URL:</th>
-      <td>{{ request.build_absolute_uri|escape }}</td>
+        <td>{{ request.build_absolute_uri|escape }}</td>
       </tr>
+      {% if raising_view_name %}
+      <tr>
+        <th>Raised by:</th>
+        <td>{{ raising_view_name }}</td>
+      </tr>
+      {% endif %}
     </table>
   </div>
   <div id="info">
diff --git a/tests/view_tests/tests/test_debug.py b/tests/view_tests/tests/test_debug.py
index 32a8304dca..7d800ae591 100644
--- a/tests/view_tests/tests/test_debug.py
+++ b/tests/view_tests/tests/test_debug.py
@@ -72,8 +72,19 @@ class DebugViewTests(TestCase):
 
     def test_404_not_in_urls(self):
         response = self.client.get('/not-in-urls')
+        self.assertNotContains(response, "Raised by:", status_code=404)
         self.assertContains(response, "<code>not-in-urls</code>, didn't match", status_code=404)
 
+    def test_technical_404(self):
+        response = self.client.get('/views/technical404/')
+        self.assertContains(response, "Raised by:", status_code=404)
+        self.assertContains(response, "view_tests.views.technical404", status_code=404)
+
+    def test_classbased_technical_404(self):
+        response = self.client.get('/views/classbased404/')
+        self.assertContains(response, "Raised by:", status_code=404)
+        self.assertContains(response, "view_tests.views.Http404View", status_code=404)
+
     def test_view_exceptions(self):
         for n in range(len(except_args)):
             self.assertRaises(BrokenException, self.client.get,
diff --git a/tests/view_tests/urls.py b/tests/view_tests/urls.py
index fdb263fa71..202f8589eb 100644
--- a/tests/view_tests/urls.py
+++ b/tests/view_tests/urls.py
@@ -57,6 +57,9 @@ urlpatterns = [
     url(r'raises404/$', views.raises404),
     url(r'raises500/$', views.raises500),
 
+    url(r'technical404/$', views.technical404, name="my404"),
+    url(r'classbased404/$', views.Http404View.as_view()),
+
     # i18n views
     url(r'^i18n/', include('django.conf.urls.i18n')),
     url(r'^jsi18n/$', i18n.javascript_catalog, js_info_dict),
diff --git a/tests/view_tests/views.py b/tests/view_tests/views.py
index 2e8a270e94..003ea76b1f 100644
--- a/tests/view_tests/views.py
+++ b/tests/view_tests/views.py
@@ -7,7 +7,7 @@ import sys
 
 from django.core.exceptions import PermissionDenied, SuspiciousOperation
 from django.core.urlresolvers import get_resolver
-from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
+from django.http import HttpResponse, HttpResponseRedirect, JsonResponse, Http404
 from django.shortcuts import render_to_response, render
 from django.template import Context, RequestContext, TemplateDoesNotExist
 from django.views.debug import technical_500_response, SafeExceptionReporterFilter
@@ -16,6 +16,8 @@ from django.views.decorators.debug import (sensitive_post_parameters,
 from django.utils._os import upath
 from django.utils.log import getLogger
 
+from django.views.generic import View
+
 from . import BrokenException, except_args
 
 dirs = (os.path.join(os.path.dirname(upath(__file__)), 'other_templates'),)
@@ -60,6 +62,15 @@ def raises404(request):
     resolver.resolve('/not-in-urls')
 
 
+def technical404(request):
+    raise Http404("Testing technical 404.")
+
+
+class Http404View(View):
+    def get(self, request):
+        raise Http404("Testing class-based technical 404.")
+
+
 def redirect(request):
     """
     Forces an HTTP redirect.