summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAymeric Augustin <aymeric.augustin@m4x.org>2012-05-17 13:54:51 +0200
committerAymeric Augustin <aymeric.augustin@m4x.org>2012-05-17 13:54:51 +0200
commitc4996df16c58b46844d2e046bca5a3d41dfcc122 (patch)
tree00c6d74c813e7771e9f26f8a244b68c62f4fa312
parent009e237cf0c25ce13e73d58feb54ef4f86e47e4e (diff)
Fixed #17449 -- Added OPTIONS to generic views.
Thanks estebistec for the report and patch.
-rw-r--r--django/views/generic/base.py12
-rw-r--r--tests/regressiontests/generic_views/base.py52
2 files changed, 62 insertions, 2 deletions
diff --git a/django/views/generic/base.py b/django/views/generic/base.py
index c32a58a349..7d3649cbff 100644
--- a/django/views/generic/base.py
+++ b/django/views/generic/base.py
@@ -79,14 +79,22 @@ class View(object):
return handler(request, *args, **kwargs)
def http_method_not_allowed(self, request, *args, **kwargs):
- allowed_methods = [m for m in self.http_method_names if hasattr(self, m)]
logger.warning('Method Not Allowed (%s): %s', request.method, request.path,
extra={
'status_code': 405,
'request': self.request
}
)
- return http.HttpResponseNotAllowed(allowed_methods)
+ return http.HttpResponseNotAllowed(self._allowed_methods())
+
+ def options(self, request, *args, **kwargs):
+ response = http.HttpResponse()
+ response['Allow'] = ', '.join(self._allowed_methods())
+ response['Content-Length'] = 0
+ return response
+
+ def _allowed_methods(self):
+ return [m.upper() for m in self.http_method_names if hasattr(self, m)]
class TemplateResponseMixin(object):
diff --git a/tests/regressiontests/generic_views/base.py b/tests/regressiontests/generic_views/base.py
index e18ed2a2a7..fc3dc05fd7 100644
--- a/tests/regressiontests/generic_views/base.py
+++ b/tests/regressiontests/generic_views/base.py
@@ -173,6 +173,58 @@ class ViewTest(unittest.TestCase):
"""
self.assertTrue(DecoratedDispatchView.as_view().is_decorated)
+ def test_head_no_get(self):
+ """
+ Test that a view class with no get responds to a HEAD request with HTTP
+ 405.
+ """
+ request = self.rf.head('/')
+ view = PostOnlyView.as_view()
+ self.assertEqual(405, view(request).status_code)
+
+ def test_options(self):
+ """
+ Test that views respond to HTTP OPTIONS requests with an Allow header
+ appropriate for the methods implemented by the view class.
+ """
+ request = self.rf.options('/')
+ view = SimpleView.as_view()
+ response = view(request)
+ self.assertEqual(200, response.status_code)
+ self.assertTrue(response['Allow'])
+
+ def test_options_for_get_view(self):
+ """
+ Test that a view implementing GET allows GET and HEAD.
+ """
+ request = self.rf.options('/')
+ view = SimpleView.as_view()
+ response = view(request)
+ self._assert_allows(response, 'GET', 'HEAD')
+
+ def test_options_for_get_and_post_view(self):
+ """
+ Test that a view implementing GET and POST allows GET, HEAD, and POST.
+ """
+ request = self.rf.options('/')
+ view = SimplePostView.as_view()
+ response = view(request)
+ self._assert_allows(response, 'GET', 'HEAD', 'POST')
+
+ def test_options_for_post_view(self):
+ """
+ Test that a view implementing POST allows POST.
+ """
+ request = self.rf.options('/')
+ view = PostOnlyView.as_view()
+ response = view(request)
+ self._assert_allows(response, 'POST')
+
+ def _assert_allows(self, response, *expected_methods):
+ "Assert allowed HTTP methods reported in the Allow response header"
+ response_allows = set(response['Allow'].split(', '))
+ self.assertEqual(set(expected_methods + ('OPTIONS',)), response_allows)
+
class TemplateViewTest(TestCase):
urls = 'regressiontests.generic_views.urls'