summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaude Paroz <claude@2xlibre.net>2019-12-15 15:30:35 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2020-01-27 08:52:40 +0100
commit7fa0fa45c5786619409036c5adce52ff6189b0ac (patch)
treee5cd944e176d80daf8748f1e5cb435344ebdb808
parent5d654e1e7104d2ce86ec1b9fe52865a7dca4b4be (diff)
Refs #30997 -- Removed HttpRequest.is_ajax() usage.
-rw-r--r--django/views/debug.py8
-rw-r--r--django/views/i18n.py2
-rw-r--r--docs/releases/3.1.txt5
-rw-r--r--docs/topics/i18n/translation.txt15
-rw-r--r--docs/topics/testing/tools.txt8
-rw-r--r--tests/view_tests/tests/test_debug.py18
-rw-r--r--tests/view_tests/tests/test_i18n.py21
7 files changed, 47 insertions, 30 deletions
diff --git a/django/views/debug.py b/django/views/debug.py
index 1761d6904a..e6b7d4d821 100644
--- a/django/views/debug.py
+++ b/django/views/debug.py
@@ -48,12 +48,12 @@ def technical_500_response(request, exc_type, exc_value, tb, status_code=500):
the values returned from sys.exc_info() and friends.
"""
reporter = get_exception_reporter_class(request)(request, exc_type, exc_value, tb)
- if request.is_ajax():
- text = reporter.get_traceback_text()
- return HttpResponse(text, status=status_code, content_type='text/plain; charset=utf-8')
- else:
+ if request.accepts('text/html'):
html = reporter.get_traceback_html()
return HttpResponse(html, status=status_code, content_type='text/html')
+ else:
+ text = reporter.get_traceback_text()
+ return HttpResponse(text, status=status_code, content_type='text/plain; charset=utf-8')
@functools.lru_cache()
diff --git a/django/views/i18n.py b/django/views/i18n.py
index 17eb4f9f61..23656a24db 100644
--- a/django/views/i18n.py
+++ b/django/views/i18n.py
@@ -33,7 +33,7 @@ def set_language(request):
"""
next_url = request.POST.get('next', request.GET.get('next'))
if (
- (next_url or not request.is_ajax()) and
+ (next_url or request.accepts('text/html')) and
not url_has_allowed_host_and_scheme(
url=next_url,
allowed_hosts={request.get_host()},
diff --git a/docs/releases/3.1.txt b/docs/releases/3.1.txt
index 3c88ecadd7..09b0b24eec 100644
--- a/docs/releases/3.1.txt
+++ b/docs/releases/3.1.txt
@@ -388,6 +388,11 @@ Miscellaneous
Django 3.1, the first request to any previously cached template fragment will
be a cache miss.
+* The logic behind the decision to return a redirection fallback or a 204 HTTP
+ response from the :func:`~django.views.i18n.set_language` view is now based
+ on the ``Accept`` HTTP header instead of the ``X-Requested-With`` HTTP header
+ presence.
+
* The compatibility imports of ``django.core.exceptions.EmptyResultSet`` in
``django.db.models.query``, ``django.db.models.sql``, and
``django.db.models.sql.datastructures`` are removed.
diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt
index 241dcea660..1dc85d801c 100644
--- a/docs/topics/i18n/translation.txt
+++ b/docs/topics/i18n/translation.txt
@@ -1803,9 +1803,18 @@ redirect to that URL will be performed. Otherwise, Django may fall back to
redirecting the user to the URL from the ``Referer`` header or, if it is not
set, to ``/``, depending on the nature of the request:
-* For AJAX requests, the fallback will be performed only if the ``next``
- parameter was set. Otherwise a 204 status code (No Content) will be returned.
-* For non-AJAX requests, the fallback will always be performed.
+* If the request accepts HTML content (based on its ``Accept`` HTTP header),
+ the fallback will always be performed.
+
+* If the request doesn't accept HTML, the fallback will be performed only if
+ the ``next`` parameter was set. Otherwise a 204 status code (No Content) will
+ be returned.
+
+.. versionchanged:: 3.1
+
+ In older versions, the distinction for the fallback is based on whether the
+ ``X-Requested-With`` header is set to the value ``XMLHttpRequest``. This is
+ set by the jQuery ``ajax()`` method.
Here's example HTML template code:
diff --git a/docs/topics/testing/tools.txt b/docs/topics/testing/tools.txt
index 2556670904..59f36d44b8 100644
--- a/docs/topics/testing/tools.txt
+++ b/docs/topics/testing/tools.txt
@@ -159,11 +159,11 @@ Use the ``django.test.Client`` class to make requests.
>>> c = Client()
>>> c.get('/customers/details/', {'name': 'fred', 'age': 7},
- ... HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+ ... HTTP_ACCEPT='application/json')
- ...will send the HTTP header ``HTTP_X_REQUESTED_WITH`` to the
- details view, which is a good way to test code paths that use the
- :meth:`django.http.HttpRequest.is_ajax()` method.
+ ...will send the HTTP header ``HTTP_ACCEPT`` to the details view, which
+ is a good way to test code paths that use the
+ :meth:`django.http.HttpRequest.accepts()` method.
.. admonition:: CGI specification
diff --git a/tests/view_tests/tests/test_debug.py b/tests/view_tests/tests/test_debug.py
index 07d8208e19..63a9142862 100644
--- a/tests/view_tests/tests/test_debug.py
+++ b/tests/view_tests/tests/test_debug.py
@@ -1247,7 +1247,7 @@ class ExceptionReporterFilterTests(ExceptionReportTestMixin, LoggingCaptureMixin
response = self.client.get(
'/raises500/',
HTTP_SECRET_HEADER='super_secret',
- HTTP_X_REQUESTED_WITH='XMLHttpRequest',
+ HTTP_ACCEPT='application/json',
)
self.assertNotIn(b'super_secret', response.content)
@@ -1289,17 +1289,17 @@ class CustomExceptionReporterFilterTests(SimpleTestCase):
)
-class AjaxResponseExceptionReporterFilter(ExceptionReportTestMixin, LoggingCaptureMixin, SimpleTestCase):
+class NonHTMLResponseExceptionReporterFilter(ExceptionReportTestMixin, LoggingCaptureMixin, SimpleTestCase):
"""
Sensitive information can be filtered out of error reports.
- Here we specifically test the plain text 500 debug-only error page served
- when it has been detected the request was sent by JS code. We don't check
- for (non)existence of frames vars in the traceback information section of
- the response content because we don't include them in these error pages.
+ The plain text 500 debug-only error page is served when it has been
+ detected the request doesn't accept HTML content. Don't check for
+ (non)existence of frames vars in the traceback information section of the
+ response content because they're not included in these error pages.
Refs #14614.
"""
- rf = RequestFactory(HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+ rf = RequestFactory(HTTP_ACCEPT='application/json')
def test_non_sensitive_request(self):
"""
@@ -1346,8 +1346,8 @@ class AjaxResponseExceptionReporterFilter(ExceptionReportTestMixin, LoggingCaptu
self.verify_unsafe_response(custom_exception_reporter_filter_view, check_for_vars=False)
@override_settings(DEBUG=True, ROOT_URLCONF='view_tests.urls')
- def test_ajax_response_encoding(self):
- response = self.client.get('/raises500/', HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+ def test_non_html_response_encoding(self):
+ response = self.client.get('/raises500/', HTTP_ACCEPT='application/json')
self.assertEqual(response['Content-Type'], 'text/plain; charset=utf-8')
diff --git a/tests/view_tests/tests/test_i18n.py b/tests/view_tests/tests/test_i18n.py
index ff64bda29d..dd07aa9f93 100644
--- a/tests/view_tests/tests/test_i18n.py
+++ b/tests/view_tests/tests/test_i18n.py
@@ -111,11 +111,12 @@ class SetLanguageTests(TestCase):
def test_setlang_performs_redirect_for_ajax_if_explicitly_requested(self):
"""
- The set_language view redirects to the "next" parameter for AJAX calls.
+ The set_language view redirects to the "next" parameter for requests
+ not accepting HTML response content.
"""
lang_code = self._get_inactive_language_code()
post_data = {'language': lang_code, 'next': '/'}
- response = self.client.post('/i18n/setlang/', post_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+ response = self.client.post('/i18n/setlang/', post_data, HTTP_ACCEPT='application/json')
self.assertRedirects(response, '/')
self.assertEqual(self.client.cookies[settings.LANGUAGE_COOKIE_NAME].value, lang_code)
with ignore_warnings(category=RemovedInDjango40Warning):
@@ -123,12 +124,12 @@ class SetLanguageTests(TestCase):
def test_setlang_doesnt_perform_a_redirect_to_referer_for_ajax(self):
"""
- The set_language view doesn't redirect to the HTTP referer header for
- AJAX calls.
+ The set_language view doesn't redirect to the HTTP referer header if
+ the request doesn't accept HTML response content.
"""
lang_code = self._get_inactive_language_code()
post_data = {'language': lang_code}
- headers = {'HTTP_REFERER': '/', 'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'}
+ headers = {'HTTP_REFERER': '/', 'HTTP_ACCEPT': 'application/json'}
response = self.client.post('/i18n/setlang/', post_data, **headers)
self.assertEqual(response.status_code, 204)
self.assertEqual(self.client.cookies[settings.LANGUAGE_COOKIE_NAME].value, lang_code)
@@ -137,11 +138,12 @@ class SetLanguageTests(TestCase):
def test_setlang_doesnt_perform_a_default_redirect_for_ajax(self):
"""
- The set_language view returns 204 for AJAX calls by default.
+ The set_language view returns 204 by default for requests not accepting
+ HTML response content.
"""
lang_code = self._get_inactive_language_code()
post_data = {'language': lang_code}
- response = self.client.post('/i18n/setlang/', post_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+ response = self.client.post('/i18n/setlang/', post_data, HTTP_ACCEPT='application/json')
self.assertEqual(response.status_code, 204)
self.assertEqual(self.client.cookies[settings.LANGUAGE_COOKIE_NAME].value, lang_code)
with ignore_warnings(category=RemovedInDjango40Warning):
@@ -149,11 +151,12 @@ class SetLanguageTests(TestCase):
def test_setlang_unsafe_next_for_ajax(self):
"""
- The fallback to root URL for the set_language view works for AJAX calls.
+ The fallback to root URL for the set_language view works for requests
+ not accepting HTML response content.
"""
lang_code = self._get_inactive_language_code()
post_data = {'language': lang_code, 'next': '//unsafe/redirection/'}
- response = self.client.post('/i18n/setlang/', post_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+ response = self.client.post('/i18n/setlang/', post_data, HTTP_ACCEPT='application/json')
self.assertEqual(response.url, '/')
self.assertEqual(self.client.cookies[settings.LANGUAGE_COOKIE_NAME].value, lang_code)